// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 package arguments import ( "os" "time" "github.com/hashicorp/terraform/internal/tfdiags" ) // Init represents the command-line arguments for the init command. type Init struct { // FromModule identifies the module to copy into the target directory before init. FromModule string // Lockfile specifies a dependency lockfile mode. Lockfile string // TestDirectory is the directory containing any test files that should be // validated alongside the main configuration. Should be relative to the // Path. TestsDirectory string // ViewType specifies which init format to use: human or JSON. ViewType ViewType // Backend specifies whether to disable backend or HCP Terraform initialization. Backend bool // Cloud specifies whether to disable backend or HCP Terraform initialization. Cloud bool // Get specifies whether to disable downloading modules for this configuration Get bool // ForceInitCopy specifies whether to suppress prompts about copying state data. ForceInitCopy bool // StateLock specifies whether hold a state lock during backend migration. StateLock bool // StateLockTimeout specifies the duration to wait for a state lock. StateLockTimeout time.Duration // Reconfigure specifies whether to disregard any existing configuration, preventing migration of any existing state Reconfigure bool // MigrateState specifies whether to attempt to copy existing state to the new backend MigrateState bool // Upgrade specifies whether to upgrade modules and plugins as part of their respective installation steps Upgrade bool // Json specifies whether to output in JSON format Json bool // IgnoreRemoteVersion specifies whether to ignore remote and local Terraform versions compatibility IgnoreRemoteVersion bool BackendConfig FlagNameValueSlice Vars *Vars // InputEnabled is used to disable interactive input for unspecified // variable and backend config values. Default is true. InputEnabled bool TargetFlags []string CompactWarnings bool PluginPath FlagStringSlice Args []string // The -enable-pluggable-state-storage-experiment flag is used in control flow logic in the init command. // TODO(SarahFrench/radeksimko): Remove this once the feature is no longer // experimental EnablePssExperiment bool // CreateDefaultWorkspace indicates whether the default workspace should be created by // Terraform when initializing a state store for the first time. CreateDefaultWorkspace bool } // ParseInit processes CLI arguments, returning an Init value and errors. // If errors are encountered, an Init value is still returned representing // the best effort interpretation of the arguments. func ParseInit(args []string, experimentsEnabled bool) (*Init, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics init := &Init{ Vars: &Vars{}, } init.BackendConfig = NewFlagNameValueSlice("-backend-config") cmdFlags := extendedFlagSet("init", nil, nil, init.Vars) cmdFlags.Var((*FlagStringSlice)(&init.TargetFlags), "target", "resource to target") cmdFlags.BoolVar(&init.InputEnabled, "input", true, "input") cmdFlags.BoolVar(&init.CompactWarnings, "compact-warnings", false, "use compact warnings") cmdFlags.BoolVar(&init.Backend, "backend", true, "") cmdFlags.BoolVar(&init.Cloud, "cloud", true, "") cmdFlags.StringVar(&init.FromModule, "from-module", "", "copy the source of the given module into the directory before init") cmdFlags.BoolVar(&init.Get, "get", true, "") cmdFlags.BoolVar(&init.ForceInitCopy, "force-copy", false, "suppress prompts about copying state data") cmdFlags.BoolVar(&init.StateLock, "lock", true, "lock state") cmdFlags.DurationVar(&init.StateLockTimeout, "lock-timeout", 0, "lock timeout") cmdFlags.BoolVar(&init.Reconfigure, "reconfigure", false, "reconfigure") cmdFlags.BoolVar(&init.MigrateState, "migrate-state", false, "migrate state") cmdFlags.BoolVar(&init.Upgrade, "upgrade", false, "") cmdFlags.StringVar(&init.Lockfile, "lockfile", "", "Set a dependency lockfile mode") cmdFlags.BoolVar(&init.IgnoreRemoteVersion, "ignore-remote-version", false, "continue even if remote and local Terraform versions are incompatible") cmdFlags.StringVar(&init.TestsDirectory, "test-directory", "tests", "test-directory") cmdFlags.BoolVar(&init.Json, "json", false, "json") cmdFlags.Var(&init.BackendConfig, "backend-config", "") cmdFlags.Var(&init.PluginPath, "plugin-dir", "plugin directory") cmdFlags.BoolVar(&init.CreateDefaultWorkspace, "create-default-workspace", true, "when -input=false, use this flag to block creation of the default workspace") // Used for enabling experimental code that's invoked before configuration is parsed. cmdFlags.BoolVar(&init.EnablePssExperiment, "enable-pluggable-state-storage-experiment", false, "Enable the pluggable state storage experiment") if err := cmdFlags.Parse(args); err != nil { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Failed to parse command-line flags", err.Error(), )) } if v := os.Getenv("TF_ENABLE_PLUGGABLE_STATE_STORAGE"); v != "" { init.EnablePssExperiment = true } if v := os.Getenv("TF_SKIP_CREATE_DEFAULT_WORKSPACE"); v != "" { // If TF_SKIP_CREATE_DEFAULT_WORKSPACE is set it will override // a -create-default-workspace=true flag that's set explicitly, // as that's indistinguishable from the default value being used. init.CreateDefaultWorkspace = false } if !experimentsEnabled { // If experiments aren't enabled then these flags should not be used. if init.EnablePssExperiment { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Cannot use -enable-pluggable-state-storage-experiment flag without experiments enabled", "Terraform cannot use the -enable-pluggable-state-storage-experiment flag (or TF_ENABLE_PLUGGABLE_STATE_STORAGE environment variable) unless experiments are enabled.", )) } if !init.CreateDefaultWorkspace { // Can only be set to false by using the flag // and we cannot identify if -create-default-workspace=true is set explicitly. diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Cannot use -create-default-workspace flag without experiments enabled", "Terraform cannot use the -create-default-workspace flag (or TF_SKIP_CREATE_DEFAULT_WORKSPACE environment variable) unless experiments are enabled.", )) } } else { // Errors using flags despite experiments being enabled. if !init.CreateDefaultWorkspace && !init.EnablePssExperiment { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Cannot use -create-default-workspace=false flag unless the pluggable state storage experiment is enabled", "Terraform cannot use the -create-default-workspace=false flag (or TF_SKIP_CREATE_DEFAULT_WORKSPACE environment variable) unless you also supply the -enable-pluggable-state-storage-experiment flag (or set the TF_ENABLE_PLUGGABLE_STATE_STORAGE environment variable).", )) } } if init.MigrateState && init.Json { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "The -migrate-state and -json options are mutually-exclusive", "Terraform cannot ask for interactive approval when -json is set. To use the -migrate-state option, disable the -json option.", )) } if init.MigrateState && init.Reconfigure { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Invalid init options", "The -migrate-state and -reconfigure options are mutually-exclusive.", )) } init.Args = cmdFlags.Args() backendFlagSet := FlagIsSet(cmdFlags, "backend") cloudFlagSet := FlagIsSet(cmdFlags, "cloud") if backendFlagSet && cloudFlagSet { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Invalid init options", "The -backend and -cloud options are aliases of one another and mutually-exclusive in their use", )) } else if backendFlagSet { init.Cloud = init.Backend } else if cloudFlagSet { init.Backend = init.Cloud } switch { case init.Json: init.ViewType = ViewJSON default: init.ViewType = ViewHuman } return init, diags }