1package command
2
3import (
4	"context"
5	"fmt"
6	"log"
7	"strings"
8
9	"github.com/hashicorp/hcl/v2"
10	"github.com/hashicorp/terraform-config-inspect/tfconfig"
11	svchost "github.com/hashicorp/terraform-svchost"
12	"github.com/posener/complete"
13	"github.com/zclconf/go-cty/cty"
14
15	"github.com/hashicorp/terraform/internal/addrs"
16	"github.com/hashicorp/terraform/internal/backend"
17	backendInit "github.com/hashicorp/terraform/internal/backend/init"
18	"github.com/hashicorp/terraform/internal/configs"
19	"github.com/hashicorp/terraform/internal/configs/configschema"
20	"github.com/hashicorp/terraform/internal/getproviders"
21	"github.com/hashicorp/terraform/internal/providercache"
22	"github.com/hashicorp/terraform/internal/states"
23	"github.com/hashicorp/terraform/internal/terraform"
24	"github.com/hashicorp/terraform/internal/tfdiags"
25	tfversion "github.com/hashicorp/terraform/version"
26)
27
28// InitCommand is a Command implementation that takes a Terraform
29// module and clones it to the working directory.
30type InitCommand struct {
31	Meta
32}
33
34func (c *InitCommand) Run(args []string) int {
35	var flagFromModule, flagLockfile string
36	var flagBackend, flagGet, flagUpgrade bool
37	var flagPluginPath FlagStringSlice
38	flagConfigExtra := newRawFlags("-backend-config")
39
40	args = c.Meta.process(args)
41	cmdFlags := c.Meta.extendedFlagSet("init")
42	cmdFlags.BoolVar(&flagBackend, "backend", true, "")
43	cmdFlags.Var(flagConfigExtra, "backend-config", "")
44	cmdFlags.StringVar(&flagFromModule, "from-module", "", "copy the source of the given module into the directory before init")
45	cmdFlags.BoolVar(&flagGet, "get", true, "")
46	cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data")
47	cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
48	cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
49	cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure")
50	cmdFlags.BoolVar(&c.migrateState, "migrate-state", false, "migrate state")
51	cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "")
52	cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory")
53	cmdFlags.StringVar(&flagLockfile, "lockfile", "", "Set a dependency lockfile mode")
54	cmdFlags.BoolVar(&c.Meta.ignoreRemoteVersion, "ignore-remote-version", false, "continue even if remote and local Terraform versions are incompatible")
55	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
56	if err := cmdFlags.Parse(args); err != nil {
57		return 1
58	}
59
60	if c.migrateState && c.reconfigure {
61		c.Ui.Error("The -migrate-state and -reconfigure options are mutually-exclusive")
62		return 1
63	}
64
65	// Copying the state only happens during backend migration, so setting
66	// -force-copy implies -migrate-state
67	if c.forceInitCopy {
68		c.migrateState = true
69	}
70
71	var diags tfdiags.Diagnostics
72
73	if len(flagPluginPath) > 0 {
74		c.pluginPath = flagPluginPath
75	}
76
77	// Validate the arg count and get the working directory
78	args = cmdFlags.Args()
79	path, err := ModulePath(args)
80	if err != nil {
81		c.Ui.Error(err.Error())
82		return 1
83	}
84
85	if err := c.storePluginPath(c.pluginPath); err != nil {
86		c.Ui.Error(fmt.Sprintf("Error saving -plugin-path values: %s", err))
87		return 1
88	}
89
90	// This will track whether we outputted anything so that we know whether
91	// to output a newline before the success message
92	var header bool
93
94	if flagFromModule != "" {
95		src := flagFromModule
96
97		empty, err := configs.IsEmptyDir(path)
98		if err != nil {
99			c.Ui.Error(fmt.Sprintf("Error validating destination directory: %s", err))
100			return 1
101		}
102		if !empty {
103			c.Ui.Error(strings.TrimSpace(errInitCopyNotEmpty))
104			return 1
105		}
106
107		c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
108			"[reset][bold]Copying configuration[reset] from %q...", src,
109		)))
110		header = true
111
112		hooks := uiModuleInstallHooks{
113			Ui:             c.Ui,
114			ShowLocalPaths: false, // since they are in a weird location for init
115		}
116
117		initDiags := c.initDirFromModule(path, src, hooks)
118		diags = diags.Append(initDiags)
119		if initDiags.HasErrors() {
120			c.showDiagnostics(diags)
121			return 1
122		}
123
124		c.Ui.Output("")
125	}
126
127	// If our directory is empty, then we're done. We can't get or set up
128	// the backend with an empty directory.
129	empty, err := configs.IsEmptyDir(path)
130	if err != nil {
131		diags = diags.Append(fmt.Errorf("Error checking configuration: %s", err))
132		c.showDiagnostics(diags)
133		return 1
134	}
135	if empty {
136		c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitEmpty)))
137		return 0
138	}
139
140	// For Terraform v0.12 we introduced a special loading mode where we would
141	// use the 0.11-syntax-compatible "earlyconfig" package as a heuristic to
142	// identify situations where it was likely that the user was trying to use
143	// 0.11-only syntax that the upgrade tool might help with.
144	//
145	// However, as the language has moved on that is no longer a suitable
146	// heuristic in Terraform 0.13 and later: other new additions to the
147	// language can cause the main loader to disagree with earlyconfig, which
148	// would lead us to give poor advice about how to respond.
149	//
150	// For that reason, we no longer use a different error message in that
151	// situation, but for now we still use both codepaths because some of our
152	// initialization functionality remains built around "earlyconfig" and
153	// so we need to still load the module via that mechanism anyway until we
154	// can do some more invasive refactoring here.
155	rootModEarly, earlyConfDiags := c.loadSingleModuleEarly(path)
156	// If _only_ the early loader encountered errors then that's unusual
157	// (it should generally be a superset of the normal loader) but we'll
158	// return those errors anyway since otherwise we'll probably get
159	// some weird behavior downstream. Errors from the early loader are
160	// generally not as high-quality since it has less context to work with.
161	if earlyConfDiags.HasErrors() {
162		c.Ui.Error(c.Colorize().Color(strings.TrimSpace(errInitConfigError)))
163		// Errors from the early loader are generally not as high-quality since
164		// it has less context to work with.
165
166		// TODO: It would be nice to check the version constraints in
167		// rootModEarly.RequiredCore and print out a hint if the module is
168		// declaring that it's not compatible with this version of Terraform,
169		// and that may be what caused earlyconfig to fail.
170		diags = diags.Append(earlyConfDiags)
171		c.showDiagnostics(diags)
172		return 1
173	}
174
175	if flagGet {
176		modsOutput, modsDiags := c.getModules(path, rootModEarly, flagUpgrade)
177		diags = diags.Append(modsDiags)
178		if modsDiags.HasErrors() {
179			c.showDiagnostics(diags)
180			return 1
181		}
182		if modsOutput {
183			header = true
184		}
185	}
186
187	// With all of the modules (hopefully) installed, we can now try to load the
188	// whole configuration tree.
189	config, confDiags := c.loadConfig(path)
190	// configDiags will be handled after the version constraint check, since an
191	// incorrect version of terraform may be producing errors for configuration
192	// constructs added in later versions.
193
194	// Before we go further, we'll check to make sure none of the modules in
195	// the configuration declare that they don't support this Terraform
196	// version, so we can produce a version-related error message rather than
197	// potentially-confusing downstream errors.
198	versionDiags := terraform.CheckCoreVersionRequirements(config)
199	if versionDiags.HasErrors() {
200		c.showDiagnostics(versionDiags)
201		return 1
202	}
203
204	diags = diags.Append(confDiags)
205	if confDiags.HasErrors() {
206		c.Ui.Error(strings.TrimSpace(errInitConfigError))
207		c.showDiagnostics(diags)
208		return 1
209	}
210
211	var back backend.Backend
212	if flagBackend {
213
214		be, backendOutput, backendDiags := c.initBackend(config.Module, flagConfigExtra)
215		diags = diags.Append(backendDiags)
216		if backendDiags.HasErrors() {
217			c.showDiagnostics(diags)
218			return 1
219		}
220		if backendOutput {
221			header = true
222		}
223		back = be
224	} else {
225		// load the previously-stored backend config
226		be, backendDiags := c.Meta.backendFromState()
227		diags = diags.Append(backendDiags)
228		if backendDiags.HasErrors() {
229			c.showDiagnostics(diags)
230			return 1
231		}
232		back = be
233	}
234
235	if back == nil {
236		// If we didn't initialize a backend then we'll try to at least
237		// instantiate one. This might fail if it wasn't already initialized
238		// by a previous run, so we must still expect that "back" may be nil
239		// in code that follows.
240		var backDiags tfdiags.Diagnostics
241		back, backDiags = c.Backend(nil)
242		if backDiags.HasErrors() {
243			// This is fine. We'll proceed with no backend, then.
244			back = nil
245		}
246	}
247
248	var state *states.State
249
250	// If we have a functional backend (either just initialized or initialized
251	// on a previous run) we'll use the current state as a potential source
252	// of provider dependencies.
253	if back != nil {
254		c.ignoreRemoteBackendVersionConflict(back)
255		workspace, err := c.Workspace()
256		if err != nil {
257			c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err))
258			return 1
259		}
260		sMgr, err := back.StateMgr(workspace)
261		if err != nil {
262			c.Ui.Error(fmt.Sprintf("Error loading state: %s", err))
263			return 1
264		}
265
266		if err := sMgr.RefreshState(); err != nil {
267			c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err))
268			return 1
269		}
270
271		state = sMgr.State()
272	}
273
274	// Now that we have loaded all modules, check the module tree for missing providers.
275	providersOutput, providersAbort, providerDiags := c.getProviders(config, state, flagUpgrade, flagPluginPath, flagLockfile)
276	diags = diags.Append(providerDiags)
277	if providersAbort || providerDiags.HasErrors() {
278		c.showDiagnostics(diags)
279		return 1
280	}
281	if providersOutput {
282		header = true
283	}
284
285	// If we outputted information, then we need to output a newline
286	// so that our success message is nicely spaced out from prior text.
287	if header {
288		c.Ui.Output("")
289	}
290
291	// If we accumulated any warnings along the way that weren't accompanied
292	// by errors then we'll output them here so that the success message is
293	// still the final thing shown.
294	c.showDiagnostics(diags)
295	c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitSuccess)))
296	if !c.RunningInAutomation {
297		// If we're not running in an automation wrapper, give the user
298		// some more detailed next steps that are appropriate for interactive
299		// shell usage.
300		c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitSuccessCLI)))
301	}
302	return 0
303}
304
305func (c *InitCommand) getModules(path string, earlyRoot *tfconfig.Module, upgrade bool) (output bool, diags tfdiags.Diagnostics) {
306	if len(earlyRoot.ModuleCalls) == 0 {
307		// Nothing to do
308		return false, nil
309	}
310
311	if upgrade {
312		c.Ui.Output(c.Colorize().Color("[reset][bold]Upgrading modules..."))
313	} else {
314		c.Ui.Output(c.Colorize().Color("[reset][bold]Initializing modules..."))
315	}
316
317	hooks := uiModuleInstallHooks{
318		Ui:             c.Ui,
319		ShowLocalPaths: true,
320	}
321	instDiags := c.installModules(path, upgrade, hooks)
322	diags = diags.Append(instDiags)
323
324	// Since module installer has modified the module manifest on disk, we need
325	// to refresh the cache of it in the loader.
326	if c.configLoader != nil {
327		if err := c.configLoader.RefreshModules(); err != nil {
328			// Should never happen
329			diags = diags.Append(tfdiags.Sourceless(
330				tfdiags.Error,
331				"Failed to read module manifest",
332				fmt.Sprintf("After installing modules, Terraform could not re-read the manifest of installed modules. This is a bug in Terraform. %s.", err),
333			))
334		}
335	}
336
337	return true, diags
338}
339
340func (c *InitCommand) initBackend(root *configs.Module, extraConfig rawFlags) (be backend.Backend, output bool, diags tfdiags.Diagnostics) {
341	c.Ui.Output(c.Colorize().Color("\n[reset][bold]Initializing the backend..."))
342
343	var backendConfig *configs.Backend
344	var backendConfigOverride hcl.Body
345	if root.Backend != nil {
346		backendType := root.Backend.Type
347		bf := backendInit.Backend(backendType)
348		if bf == nil {
349			diags = diags.Append(&hcl.Diagnostic{
350				Severity: hcl.DiagError,
351				Summary:  "Unsupported backend type",
352				Detail:   fmt.Sprintf("There is no backend type named %q.", backendType),
353				Subject:  &root.Backend.TypeRange,
354			})
355			return nil, true, diags
356		}
357
358		b := bf()
359		backendSchema := b.ConfigSchema()
360		backendConfig = root.Backend
361
362		var overrideDiags tfdiags.Diagnostics
363		backendConfigOverride, overrideDiags = c.backendConfigOverrideBody(extraConfig, backendSchema)
364		diags = diags.Append(overrideDiags)
365		if overrideDiags.HasErrors() {
366			return nil, true, diags
367		}
368	} else {
369		// If the user supplied a -backend-config on the CLI but no backend
370		// block was found in the configuration, it's likely - but not
371		// necessarily - a mistake. Return a warning.
372		if !extraConfig.Empty() {
373			diags = diags.Append(tfdiags.Sourceless(
374				tfdiags.Warning,
375				"Missing backend configuration",
376				`-backend-config was used without a "backend" block in the configuration.
377
378If you intended to override the default local backend configuration,
379no action is required, but you may add an explicit backend block to your
380configuration to clear this warning:
381
382terraform {
383  backend "local" {}
384}
385
386However, if you intended to override a defined backend, please verify that
387the backend configuration is present and valid.
388`,
389			))
390		}
391	}
392
393	opts := &BackendOpts{
394		Config:         backendConfig,
395		ConfigOverride: backendConfigOverride,
396		Init:           true,
397	}
398
399	back, backDiags := c.Backend(opts)
400	diags = diags.Append(backDiags)
401	return back, true, diags
402}
403
404// Load the complete module tree, and fetch any missing providers.
405// This method outputs its own Ui.
406func (c *InitCommand) getProviders(config *configs.Config, state *states.State, upgrade bool, pluginDirs []string, flagLockfile string) (output, abort bool, diags tfdiags.Diagnostics) {
407	// Dev overrides cause the result of "terraform init" to be irrelevant for
408	// any overridden providers, so we'll warn about it to avoid later
409	// confusion when Terraform ends up using a different provider than the
410	// lock file called for.
411	diags = diags.Append(c.providerDevOverrideInitWarnings())
412
413	// First we'll collect all the provider dependencies we can see in the
414	// configuration and the state.
415	reqs, hclDiags := config.ProviderRequirements()
416	diags = diags.Append(hclDiags)
417	if hclDiags.HasErrors() {
418		return false, true, diags
419	}
420	if state != nil {
421		stateReqs := state.ProviderRequirements()
422		reqs = reqs.Merge(stateReqs)
423	}
424
425	for providerAddr := range reqs {
426		if providerAddr.IsLegacy() {
427			diags = diags.Append(tfdiags.Sourceless(
428				tfdiags.Error,
429				"Invalid legacy provider address",
430				fmt.Sprintf(
431					"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.",
432					providerAddr.Type,
433				),
434			))
435		}
436	}
437
438	previousLocks, moreDiags := c.lockedDependencies()
439	diags = diags.Append(moreDiags)
440
441	if diags.HasErrors() {
442		return false, true, diags
443	}
444
445	var inst *providercache.Installer
446	if len(pluginDirs) == 0 {
447		// By default we use a source that looks for providers in all of the
448		// standard locations, possibly customized by the user in CLI config.
449		inst = c.providerInstaller()
450	} else {
451		// If the user passes at least one -plugin-dir then that circumvents
452		// the usual sources and forces Terraform to consult only the given
453		// directories. Anything not available in one of those directories
454		// is not available for installation.
455		source := c.providerCustomLocalDirectorySource(pluginDirs)
456		inst = c.providerInstallerCustomSource(source)
457
458		// The default (or configured) search paths are logged earlier, in provider_source.go
459		// Log that those are being overridden by the `-plugin-dir` command line options
460		log.Println("[DEBUG] init: overriding provider plugin search paths")
461		log.Printf("[DEBUG] will search for provider plugins in %s", pluginDirs)
462	}
463
464	// Installation can be aborted by interruption signals
465	ctx, done := c.InterruptibleContext()
466	defer done()
467
468	// Because we're currently just streaming a series of events sequentially
469	// into the terminal, we're showing only a subset of the events to keep
470	// things relatively concise. Later it'd be nice to have a progress UI
471	// where statuses update in-place, but we can't do that as long as we
472	// are shimming our vt100 output to the legacy console API on Windows.
473	evts := &providercache.InstallerEvents{
474		PendingProviders: func(reqs map[addrs.Provider]getproviders.VersionConstraints) {
475			c.Ui.Output(c.Colorize().Color(
476				"\n[reset][bold]Initializing provider plugins...",
477			))
478		},
479		ProviderAlreadyInstalled: func(provider addrs.Provider, selectedVersion getproviders.Version) {
480			c.Ui.Info(fmt.Sprintf("- Using previously-installed %s v%s", provider.ForDisplay(), selectedVersion))
481		},
482		BuiltInProviderAvailable: func(provider addrs.Provider) {
483			c.Ui.Info(fmt.Sprintf("- %s is built in to Terraform", provider.ForDisplay()))
484		},
485		BuiltInProviderFailure: func(provider addrs.Provider, err error) {
486			diags = diags.Append(tfdiags.Sourceless(
487				tfdiags.Error,
488				"Invalid dependency on built-in provider",
489				fmt.Sprintf("Cannot use %s: %s.", provider.ForDisplay(), err),
490			))
491		},
492		QueryPackagesBegin: func(provider addrs.Provider, versionConstraints getproviders.VersionConstraints, locked bool) {
493			if locked {
494				c.Ui.Info(fmt.Sprintf("- Reusing previous version of %s from the dependency lock file", provider.ForDisplay()))
495			} else {
496				if len(versionConstraints) > 0 {
497					c.Ui.Info(fmt.Sprintf("- Finding %s versions matching %q...", provider.ForDisplay(), getproviders.VersionConstraintsString(versionConstraints)))
498				} else {
499					c.Ui.Info(fmt.Sprintf("- Finding latest version of %s...", provider.ForDisplay()))
500				}
501			}
502		},
503		LinkFromCacheBegin: func(provider addrs.Provider, version getproviders.Version, cacheRoot string) {
504			c.Ui.Info(fmt.Sprintf("- Using %s v%s from the shared cache directory", provider.ForDisplay(), version))
505		},
506		FetchPackageBegin: func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation) {
507			c.Ui.Info(fmt.Sprintf("- Installing %s v%s...", provider.ForDisplay(), version))
508		},
509		QueryPackagesFailure: func(provider addrs.Provider, err error) {
510			switch errorTy := err.(type) {
511			case getproviders.ErrProviderNotFound:
512				sources := errorTy.Sources
513				displaySources := make([]string, len(sources))
514				for i, source := range sources {
515					displaySources[i] = fmt.Sprintf("  - %s", source)
516				}
517				diags = diags.Append(tfdiags.Sourceless(
518					tfdiags.Error,
519					"Failed to query available provider packages",
520					fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s\n\n%s",
521						provider.ForDisplay(), err, strings.Join(displaySources, "\n"),
522					),
523				))
524			case getproviders.ErrRegistryProviderNotKnown:
525				// We might be able to suggest an alternative provider to use
526				// instead of this one.
527				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    terraform providers", provider.ForDisplay())
528				alternative := getproviders.MissingProviderSuggestion(ctx, provider, inst.ProviderSource(), reqs)
529				if alternative != provider {
530					suggestion = fmt.Sprintf(
531						"\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    terraform providers",
532						alternative.ForDisplay(), provider.ForDisplay(),
533					)
534				}
535
536				diags = diags.Append(tfdiags.Sourceless(
537					tfdiags.Error,
538					"Failed to query available provider packages",
539					fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s%s",
540						provider.ForDisplay(), err, suggestion,
541					),
542				))
543			case getproviders.ErrHostNoProviders:
544				switch {
545				case errorTy.Hostname == svchost.Hostname("github.com") && !errorTy.HasOtherVersion:
546					// If a user copies the URL of a GitHub repository into
547					// the source argument and removes the schema to make it
548					// provider-address-shaped then that's one way we can end up
549					// here. We'll use a specialized error message in anticipation
550					// of that mistake. We only do this if github.com isn't a
551					// provider registry, to allow for the (admittedly currently
552					// rather unlikely) possibility that github.com starts being
553					// a real Terraform provider registry in the future.
554					diags = diags.Append(tfdiags.Sourceless(
555						tfdiags.Error,
556						"Invalid provider registry host",
557						fmt.Sprintf("The given source address %q specifies a GitHub repository rather than a Terraform provider. Refer to the documentation of the provider to find the correct source address to use.",
558							provider.String(),
559						),
560					))
561
562				case errorTy.HasOtherVersion:
563					diags = diags.Append(tfdiags.Sourceless(
564						tfdiags.Error,
565						"Invalid provider registry host",
566						fmt.Sprintf("The host %q given in in provider source address %q does not offer a Terraform provider registry that is compatible with this Terraform version, but it may be compatible with a different Terraform version.",
567							errorTy.Hostname, provider.String(),
568						),
569					))
570
571				default:
572					diags = diags.Append(tfdiags.Sourceless(
573						tfdiags.Error,
574						"Invalid provider registry host",
575						fmt.Sprintf("The host %q given in in provider source address %q does not offer a Terraform provider registry.",
576							errorTy.Hostname, provider.String(),
577						),
578					))
579				}
580
581			case getproviders.ErrRequestCanceled:
582				// We don't attribute cancellation to any particular operation,
583				// but rather just emit a single general message about it at
584				// the end, by checking ctx.Err().
585
586			default:
587				diags = diags.Append(tfdiags.Sourceless(
588					tfdiags.Error,
589					"Failed to query available provider packages",
590					fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s",
591						provider.ForDisplay(), err,
592					),
593				))
594			}
595
596		},
597		QueryPackagesWarning: func(provider addrs.Provider, warnings []string) {
598			displayWarnings := make([]string, len(warnings))
599			for i, warning := range warnings {
600				displayWarnings[i] = fmt.Sprintf("- %s", warning)
601			}
602
603			diags = diags.Append(tfdiags.Sourceless(
604				tfdiags.Warning,
605				"Additional provider information from registry",
606				fmt.Sprintf("The remote registry returned warnings for %s:\n%s",
607					provider.String(),
608					strings.Join(displayWarnings, "\n"),
609				),
610			))
611		},
612		LinkFromCacheFailure: func(provider addrs.Provider, version getproviders.Version, err error) {
613			diags = diags.Append(tfdiags.Sourceless(
614				tfdiags.Error,
615				"Failed to install provider from shared cache",
616				fmt.Sprintf("Error while importing %s v%s from the shared cache directory: %s.", provider.ForDisplay(), version, err),
617			))
618		},
619		FetchPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) {
620			const summaryIncompatible = "Incompatible provider version"
621			switch err := err.(type) {
622			case getproviders.ErrProtocolNotSupported:
623				closestAvailable := err.Suggestion
624				switch {
625				case closestAvailable == getproviders.UnspecifiedVersion:
626					diags = diags.Append(tfdiags.Sourceless(
627						tfdiags.Error,
628						summaryIncompatible,
629						fmt.Sprintf(errProviderVersionIncompatible, provider.String()),
630					))
631				case version.GreaterThan(closestAvailable):
632					diags = diags.Append(tfdiags.Sourceless(
633						tfdiags.Error,
634						summaryIncompatible,
635						fmt.Sprintf(providerProtocolTooNew, provider.ForDisplay(),
636							version, tfversion.String(), closestAvailable, closestAvailable,
637							getproviders.VersionConstraintsString(reqs[provider]),
638						),
639					))
640				default: // version is less than closestAvailable
641					diags = diags.Append(tfdiags.Sourceless(
642						tfdiags.Error,
643						summaryIncompatible,
644						fmt.Sprintf(providerProtocolTooOld, provider.ForDisplay(),
645							version, tfversion.String(), closestAvailable, closestAvailable,
646							getproviders.VersionConstraintsString(reqs[provider]),
647						),
648					))
649				}
650			case getproviders.ErrPlatformNotSupported:
651				switch {
652				case err.MirrorURL != nil:
653					// If we're installing from a mirror then it may just be
654					// the mirror lacking the package, rather than it being
655					// unavailable from upstream.
656					diags = diags.Append(tfdiags.Sourceless(
657						tfdiags.Error,
658						summaryIncompatible,
659						fmt.Sprintf(
660							"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 Terraform 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.",
661							err.MirrorURL, err.Provider, err.Version, err.Platform,
662							err.Provider.Hostname,
663						),
664					))
665				default:
666					diags = diags.Append(tfdiags.Sourceless(
667						tfdiags.Error,
668						summaryIncompatible,
669						fmt.Sprintf(
670							"Provider %s v%s does not have a package available for your current platform, %s.\n\nProvider releases are separate from Terraform CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.",
671							err.Provider, err.Version, err.Platform,
672						),
673					))
674				}
675
676			case getproviders.ErrRequestCanceled:
677				// We don't attribute cancellation to any particular operation,
678				// but rather just emit a single general message about it at
679				// the end, by checking ctx.Err().
680
681			default:
682				// We can potentially end up in here under cancellation too,
683				// in spite of our getproviders.ErrRequestCanceled case above,
684				// because not all of the outgoing requests we do under the
685				// "fetch package" banner are source metadata requests.
686				// In that case we will emit a redundant error here about
687				// the request being cancelled, but we'll still detect it
688				// as a cancellation after the installer returns and do the
689				// normal cancellation handling.
690
691				diags = diags.Append(tfdiags.Sourceless(
692					tfdiags.Error,
693					"Failed to install provider",
694					fmt.Sprintf("Error while installing %s v%s: %s", provider.ForDisplay(), version, err),
695				))
696			}
697		},
698		FetchPackageSuccess: func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult) {
699			var keyID string
700			if authResult != nil && authResult.ThirdPartySigned() {
701				keyID = authResult.KeyID
702			}
703			if keyID != "" {
704				keyID = c.Colorize().Color(fmt.Sprintf(", key ID [reset][bold]%s[reset]", keyID))
705			}
706
707			c.Ui.Info(fmt.Sprintf("- Installed %s v%s (%s%s)", provider.ForDisplay(), version, authResult, keyID))
708		},
709		ProvidersFetched: func(authResults map[addrs.Provider]*getproviders.PackageAuthenticationResult) {
710			thirdPartySigned := false
711			for _, authResult := range authResults {
712				if authResult.ThirdPartySigned() {
713					thirdPartySigned = true
714					break
715				}
716			}
717			if thirdPartySigned {
718				c.Ui.Info(fmt.Sprintf("\nPartner and community providers are signed by their developers.\n" +
719					"If you'd like to know more about provider signing, you can read about it here:\n" +
720					"https://www.terraform.io/docs/cli/plugins/signing.html"))
721			}
722		},
723		HashPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) {
724			diags = diags.Append(tfdiags.Sourceless(
725				tfdiags.Error,
726				"Failed to validate installed provider",
727				fmt.Sprintf(
728					"Validating provider %s v%s failed: %s",
729					provider.ForDisplay(),
730					version,
731					err,
732				),
733			))
734		},
735	}
736	ctx = evts.OnContext(ctx)
737
738	mode := providercache.InstallNewProvidersOnly
739	if upgrade {
740		if flagLockfile == "readonly" {
741			c.Ui.Error("The -upgrade flag conflicts with -lockfile=readonly.")
742			return true, true, diags
743		}
744
745		mode = providercache.InstallUpgrades
746	}
747	newLocks, err := inst.EnsureProviderVersions(ctx, previousLocks, reqs, mode)
748	if ctx.Err() == context.Canceled {
749		c.showDiagnostics(diags)
750		c.Ui.Error("Provider installation was canceled by an interrupt signal.")
751		return true, true, diags
752	}
753	if err != nil {
754		// The errors captured in "err" should be redundant with what we
755		// received via the InstallerEvents callbacks above, so we'll
756		// just return those as long as we have some.
757		if !diags.HasErrors() {
758			diags = diags.Append(err)
759		}
760
761		return true, true, diags
762	}
763
764	// If the provider dependencies have changed since the last run then we'll
765	// say a little about that in case the reader wasn't expecting a change.
766	// (When we later integrate module dependencies into the lock file we'll
767	// probably want to refactor this so that we produce one lock-file related
768	// message for all changes together, but this is here for now just because
769	// it's the smallest change relative to what came before it, which was
770	// a hidden JSON file specifically for tracking providers.)
771	if !newLocks.Equal(previousLocks) {
772		// if readonly mode
773		if flagLockfile == "readonly" {
774			// check if required provider dependences change
775			if !newLocks.EqualProviderAddress(previousLocks) {
776				diags = diags.Append(tfdiags.Sourceless(
777					tfdiags.Error,
778					`Provider dependency changes detected`,
779					`Changes to the required provider dependencies were detected, but the lock file is read-only. To use and record these requirements, run "terraform init" without the "-lockfile=readonly" flag.`,
780				))
781				return true, true, diags
782			}
783
784			// suppress updating the file to record any new information it learned,
785			// such as a hash using a new scheme.
786			diags = diags.Append(tfdiags.Sourceless(
787				tfdiags.Warning,
788				`Provider lock file not updated`,
789				`Changes to the provider selections were detected, but not saved in the .terraform.lock.hcl file. To record these selections, run "terraform init" without the "-lockfile=readonly" flag.`,
790			))
791			return true, false, diags
792		}
793
794		if previousLocks.Empty() {
795			// A change from empty to non-empty is special because it suggests
796			// we're running "terraform init" for the first time against a
797			// new configuration. In that case we'll take the opportunity to
798			// say a little about what the dependency lock file is, for new
799			// users or those who are upgrading from a previous Terraform
800			// version that didn't have dependency lock files.
801			c.Ui.Output(c.Colorize().Color(`
802Terraform has created a lock file [bold].terraform.lock.hcl[reset] to record the provider
803selections it made above. Include this file in your version control repository
804so that Terraform can guarantee to make the same selections by default when
805you run "terraform init" in the future.`))
806		} else {
807			c.Ui.Output(c.Colorize().Color(`
808Terraform has made some changes to the provider dependency selections recorded
809in the .terraform.lock.hcl file. Review those changes and commit them to your
810version control system if they represent changes you intended to make.`))
811		}
812
813		moreDiags = c.replaceLockedDependencies(newLocks)
814		diags = diags.Append(moreDiags)
815	}
816
817	return true, false, diags
818}
819
820// backendConfigOverrideBody interprets the raw values of -backend-config
821// arguments into a hcl Body that should override the backend settings given
822// in the configuration.
823//
824// If the result is nil then no override needs to be provided.
825//
826// If the returned diagnostics contains errors then the returned body may be
827// incomplete or invalid.
828func (c *InitCommand) backendConfigOverrideBody(flags rawFlags, schema *configschema.Block) (hcl.Body, tfdiags.Diagnostics) {
829	items := flags.AllItems()
830	if len(items) == 0 {
831		return nil, nil
832	}
833
834	var ret hcl.Body
835	var diags tfdiags.Diagnostics
836	synthVals := make(map[string]cty.Value)
837
838	mergeBody := func(newBody hcl.Body) {
839		if ret == nil {
840			ret = newBody
841		} else {
842			ret = configs.MergeBodies(ret, newBody)
843		}
844	}
845	flushVals := func() {
846		if len(synthVals) == 0 {
847			return
848		}
849		newBody := configs.SynthBody("-backend-config=...", synthVals)
850		mergeBody(newBody)
851		synthVals = make(map[string]cty.Value)
852	}
853
854	if len(items) == 1 && items[0].Value == "" {
855		// Explicitly remove all -backend-config options.
856		// We do this by setting an empty but non-nil ConfigOverrides.
857		return configs.SynthBody("-backend-config=''", synthVals), diags
858	}
859
860	for _, item := range items {
861		eq := strings.Index(item.Value, "=")
862
863		if eq == -1 {
864			// The value is interpreted as a filename.
865			newBody, fileDiags := c.loadHCLFile(item.Value)
866			diags = diags.Append(fileDiags)
867			if fileDiags.HasErrors() {
868				continue
869			}
870			// Generate an HCL body schema for the backend block.
871			var bodySchema hcl.BodySchema
872			for name := range schema.Attributes {
873				// We intentionally ignore the `Required` attribute here
874				// because backend config override files can be partial. The
875				// goal is to make sure we're not loading a file with
876				// extraneous attributes or blocks.
877				bodySchema.Attributes = append(bodySchema.Attributes, hcl.AttributeSchema{
878					Name: name,
879				})
880			}
881			for name, block := range schema.BlockTypes {
882				var labelNames []string
883				if block.Nesting == configschema.NestingMap {
884					labelNames = append(labelNames, "key")
885				}
886				bodySchema.Blocks = append(bodySchema.Blocks, hcl.BlockHeaderSchema{
887					Type:       name,
888					LabelNames: labelNames,
889				})
890			}
891			// Verify that the file body matches the expected backend schema.
892			_, schemaDiags := newBody.Content(&bodySchema)
893			diags = diags.Append(schemaDiags)
894			if schemaDiags.HasErrors() {
895				continue
896			}
897			flushVals() // deal with any accumulated individual values first
898			mergeBody(newBody)
899		} else {
900			name := item.Value[:eq]
901			rawValue := item.Value[eq+1:]
902			attrS := schema.Attributes[name]
903			if attrS == nil {
904				diags = diags.Append(tfdiags.Sourceless(
905					tfdiags.Error,
906					"Invalid backend configuration argument",
907					fmt.Sprintf("The backend configuration argument %q given on the command line is not expected for the selected backend type.", name),
908				))
909				continue
910			}
911			value, valueDiags := configValueFromCLI(item.String(), rawValue, attrS.Type)
912			diags = diags.Append(valueDiags)
913			if valueDiags.HasErrors() {
914				continue
915			}
916			synthVals[name] = value
917		}
918	}
919
920	flushVals()
921
922	return ret, diags
923}
924
925func (c *InitCommand) AutocompleteArgs() complete.Predictor {
926	return complete.PredictDirs("")
927}
928
929func (c *InitCommand) AutocompleteFlags() complete.Flags {
930	return complete.Flags{
931		"-backend":        completePredictBoolean,
932		"-backend-config": complete.PredictFiles("*.tfvars"), // can also be key=value, but we can't "predict" that
933		"-force-copy":     complete.PredictNothing,
934		"-from-module":    completePredictModuleSource,
935		"-get":            completePredictBoolean,
936		"-input":          completePredictBoolean,
937		"-lock":           completePredictBoolean,
938		"-lock-timeout":   complete.PredictAnything,
939		"-no-color":       complete.PredictNothing,
940		"-plugin-dir":     complete.PredictDirs(""),
941		"-reconfigure":    complete.PredictNothing,
942		"-migrate-state":  complete.PredictNothing,
943		"-upgrade":        completePredictBoolean,
944	}
945}
946
947func (c *InitCommand) Help() string {
948	helpText := `
949Usage: terraform [global options] init [options]
950
951  Initialize a new or existing Terraform working directory by creating
952  initial files, loading any remote state, downloading modules, etc.
953
954  This is the first command that should be run for any new or existing
955  Terraform configuration per machine. This sets up all the local data
956  necessary to run Terraform that is typically not committed to version
957  control.
958
959  This command is always safe to run multiple times. Though subsequent runs
960  may give errors, this command will never delete your configuration or
961  state. Even so, if you have important information, please back it up prior
962  to running this command, just in case.
963
964Options:
965
966  -backend=false          Disable backend initialization for this configuration
967                          and use the previously initialized backend instead.
968
969  -backend-config=path    This can be either a path to an HCL file with key/value
970                          assignments (same format as terraform.tfvars) or a
971                          'key=value' format. This is merged with what is in the
972                          configuration file. This can be specified multiple
973                          times. The backend type must be in the configuration
974                          itself.
975
976  -force-copy             Suppress prompts about copying state data. This is
977                          equivalent to providing a "yes" to all confirmation
978                          prompts.
979
980  -from-module=SOURCE     Copy the contents of the given module into the target
981                          directory before initialization.
982
983  -get=false              Disable downloading modules for this configuration.
984
985  -input=false            Disable prompting for missing backend configuration
986                          values. This will result in an error if the backend
987                          configuration is not fully specified.
988
989  -lock=false             Don't hold a state lock during backend migration.
990                          This is dangerous if others might concurrently run
991                          commands against the same workspace.
992
993  -lock-timeout=0s        Duration to retry a state lock.
994
995  -no-color               If specified, output won't contain any color.
996
997  -plugin-dir             Directory containing plugin binaries. This overrides all
998                          default search paths for plugins, and prevents the
999                          automatic installation of plugins. This flag can be used
1000                          multiple times.
1001
1002  -reconfigure            Reconfigure the backend, ignoring any saved
1003                          configuration.
1004
1005  -migrate-state          Reconfigure the backend, and attempt to migrate any
1006                          existing state.
1007
1008  -upgrade                Install the latest module and provider versions
1009                          allowed within configured constraints, overriding the
1010                          default behavior of selecting exactly the version
1011                          recorded in the dependency lockfile.
1012
1013  -lockfile=MODE          Set a dependency lockfile mode.
1014                          Currently only "readonly" is valid.
1015
1016  -ignore-remote-version  A rare option used for the remote backend only. See
1017                          the remote backend documentation for more information.
1018
1019`
1020	return strings.TrimSpace(helpText)
1021}
1022
1023func (c *InitCommand) Synopsis() string {
1024	return "Prepare your working directory for other commands"
1025}
1026
1027const errInitConfigError = `
1028[reset]There are some problems with the configuration, described below.
1029
1030The Terraform configuration must be valid before initialization so that
1031Terraform can determine which modules and providers need to be installed.
1032`
1033
1034const errInitCopyNotEmpty = `
1035The working directory already contains files. The -from-module option requires
1036an empty directory into which a copy of the referenced module will be placed.
1037
1038To initialize the configuration already in this working directory, omit the
1039-from-module option.
1040`
1041
1042const outputInitEmpty = `
1043[reset][bold]Terraform initialized in an empty directory![reset]
1044
1045The directory has no Terraform configuration files. You may begin working
1046with Terraform immediately by creating Terraform configuration files.
1047`
1048
1049const outputInitSuccess = `
1050[reset][bold][green]Terraform has been successfully initialized![reset][green]
1051`
1052
1053const outputInitSuccessCLI = `[reset][green]
1054You may now begin working with Terraform. Try running "terraform plan" to see
1055any changes that are required for your infrastructure. All Terraform commands
1056should now work.
1057
1058If you ever set or change modules or backend configuration for Terraform,
1059rerun this command to reinitialize your working directory. If you forget, other
1060commands will detect it and remind you to do so if necessary.
1061`
1062
1063// providerProtocolTooOld is a message sent to the CLI UI if the provider's
1064// supported protocol versions are too old for the user's version of terraform,
1065// but a newer version of the provider is compatible.
1066const providerProtocolTooOld = `Provider %q v%s is not compatible with Terraform %s.
1067Provider version %s is the latest compatible version. Select it with the following version constraint:
1068	version = %q
1069
1070Terraform checked all of the plugin versions matching the given constraint:
1071	%s
1072
1073Consult the documentation for this provider for more information on compatibility between provider and Terraform versions.
1074`
1075
1076// providerProtocolTooNew is a message sent to the CLI UI if the provider's
1077// supported protocol versions are too new for the user's version of terraform,
1078// and the user could either upgrade terraform or choose an older version of the
1079// provider.
1080const providerProtocolTooNew = `Provider %q v%s is not compatible with Terraform %s.
1081You need to downgrade to v%s or earlier. Select it with the following constraint:
1082	version = %q
1083
1084Terraform checked all of the plugin versions matching the given constraint:
1085	%s
1086
1087Consult the documentation for this provider for more information on compatibility between provider and Terraform versions.
1088Alternatively, upgrade to the latest version of Terraform for compatibility with newer provider releases.
1089`
1090
1091// No version of the provider is compatible.
1092const errProviderVersionIncompatible = `No compatible versions of provider %s were found.`
1093