github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/command/meta_backend.go (about)

     1  package command
     2  
     3  // This file contains all the Backend-related function calls on Meta,
     4  // exported and private.
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"log"
    11  	"path/filepath"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/hashicorp/hcl/v2"
    16  	"github.com/hashicorp/hcl/v2/hcldec"
    17  	"github.com/hashicorp/terraform/internal/backend"
    18  	remoteBackend "github.com/hashicorp/terraform/internal/backend/remote"
    19  	"github.com/hashicorp/terraform/internal/command/arguments"
    20  	"github.com/hashicorp/terraform/internal/command/clistate"
    21  	"github.com/hashicorp/terraform/internal/command/views"
    22  	"github.com/hashicorp/terraform/internal/configs"
    23  	"github.com/hashicorp/terraform/internal/plans"
    24  	"github.com/hashicorp/terraform/internal/states/statemgr"
    25  	"github.com/hashicorp/terraform/internal/terraform"
    26  	"github.com/hashicorp/terraform/internal/tfdiags"
    27  	"github.com/zclconf/go-cty/cty"
    28  	ctyjson "github.com/zclconf/go-cty/cty/json"
    29  
    30  	backendInit "github.com/hashicorp/terraform/internal/backend/init"
    31  	backendLocal "github.com/hashicorp/terraform/internal/backend/local"
    32  	legacy "github.com/hashicorp/terraform/internal/legacy/terraform"
    33  )
    34  
    35  // BackendOpts are the options used to initialize a backend.Backend.
    36  type BackendOpts struct {
    37  	// Config is a representation of the backend configuration block given in
    38  	// the root module, or nil if no such block is present.
    39  	Config *configs.Backend
    40  
    41  	// ConfigOverride is an hcl.Body that, if non-nil, will be used with
    42  	// configs.MergeBodies to override the type-specific backend configuration
    43  	// arguments in Config.
    44  	ConfigOverride hcl.Body
    45  
    46  	// Init should be set to true if initialization is allowed. If this is
    47  	// false, then any configuration that requires configuration will show
    48  	// an error asking the user to reinitialize.
    49  	Init bool
    50  
    51  	// ForceLocal will force a purely local backend, including state.
    52  	// You probably don't want to set this.
    53  	ForceLocal bool
    54  }
    55  
    56  // Backend initializes and returns the backend for this CLI session.
    57  //
    58  // The backend is used to perform the actual Terraform operations. This
    59  // abstraction enables easily sliding in new Terraform behavior such as
    60  // remote state storage, remote operations, etc. while allowing the CLI
    61  // to remain mostly identical.
    62  //
    63  // This will initialize a new backend for each call, which can carry some
    64  // overhead with it. Please reuse the returned value for optimal behavior.
    65  //
    66  // Only one backend should be used per Meta. This function is stateful
    67  // and is unsafe to create multiple backends used at once. This function
    68  // can be called multiple times with each backend being "live" (usable)
    69  // one at a time.
    70  //
    71  // A side-effect of this method is the population of m.backendState, recording
    72  // the final resolved backend configuration after dealing with overrides from
    73  // the "terraform init" command line, etc.
    74  func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics) {
    75  	var diags tfdiags.Diagnostics
    76  
    77  	// If no opts are set, then initialize
    78  	if opts == nil {
    79  		opts = &BackendOpts{}
    80  	}
    81  
    82  	// Initialize a backend from the config unless we're forcing a purely
    83  	// local operation.
    84  	var b backend.Backend
    85  	if !opts.ForceLocal {
    86  		var backendDiags tfdiags.Diagnostics
    87  		b, backendDiags = m.backendFromConfig(opts)
    88  		diags = diags.Append(backendDiags)
    89  
    90  		if opts.Init && b != nil && !diags.HasErrors() {
    91  			// Its possible that the currently selected workspace doesn't exist, so
    92  			// we call selectWorkspace to ensure an existing workspace is selected.
    93  			if err := m.selectWorkspace(b); err != nil {
    94  				diags = diags.Append(err)
    95  			}
    96  		}
    97  
    98  		if diags.HasErrors() {
    99  			return nil, diags
   100  		}
   101  
   102  		log.Printf("[TRACE] Meta.Backend: instantiated backend of type %T", b)
   103  	}
   104  
   105  	// Set up the CLI opts we pass into backends that support it.
   106  	cliOpts, err := m.backendCLIOpts()
   107  	if err != nil {
   108  		diags = diags.Append(err)
   109  		return nil, diags
   110  	}
   111  	cliOpts.Validation = true
   112  
   113  	// If the backend supports CLI initialization, do it.
   114  	if cli, ok := b.(backend.CLI); ok {
   115  		if err := cli.CLIInit(cliOpts); err != nil {
   116  			diags = diags.Append(fmt.Errorf(
   117  				"Error initializing backend %T: %s\n\n"+
   118  					"This is a bug; please report it to the backend developer",
   119  				b, err,
   120  			))
   121  			return nil, diags
   122  		}
   123  	}
   124  
   125  	// If the result of loading the backend is an enhanced backend,
   126  	// then return that as-is. This works even if b == nil (it will be !ok).
   127  	if enhanced, ok := b.(backend.Enhanced); ok {
   128  		log.Printf("[TRACE] Meta.Backend: backend %T supports operations", b)
   129  		return enhanced, nil
   130  	}
   131  
   132  	// We either have a non-enhanced backend or no backend configured at
   133  	// all. In either case, we use local as our enhanced backend and the
   134  	// non-enhanced (if any) as the state backend.
   135  
   136  	if !opts.ForceLocal {
   137  		log.Printf("[TRACE] Meta.Backend: backend %T does not support operations, so wrapping it in a local backend", b)
   138  	}
   139  
   140  	// Build the local backend
   141  	local := backendLocal.NewWithBackend(b)
   142  	if err := local.CLIInit(cliOpts); err != nil {
   143  		// Local backend isn't allowed to fail. It would be a bug.
   144  		panic(err)
   145  	}
   146  
   147  	// If we got here from backendFromConfig returning nil then m.backendState
   148  	// won't be set, since that codepath considers that to be no backend at all,
   149  	// but our caller considers that to be the local backend with no config
   150  	// and so we'll synthesize a backend state so other code doesn't need to
   151  	// care about this special case.
   152  	//
   153  	// FIXME: We should refactor this so that we more directly and explicitly
   154  	// treat the local backend as the default, including in the UI shown to
   155  	// the user, since the local backend should only be used when learning or
   156  	// in exceptional cases and so it's better to help the user learn that
   157  	// by introducing it as a concept.
   158  	if m.backendState == nil {
   159  		// NOTE: This synthetic object is intentionally _not_ retained in the
   160  		// on-disk record of the backend configuration, which was already dealt
   161  		// with inside backendFromConfig, because we still need that codepath
   162  		// to be able to recognize the lack of a config as distinct from
   163  		// explicitly setting local until we do some more refactoring here.
   164  		m.backendState = &legacy.BackendState{
   165  			Type:      "local",
   166  			ConfigRaw: json.RawMessage("{}"),
   167  		}
   168  	}
   169  
   170  	return local, nil
   171  }
   172  
   173  // selectWorkspace gets a list of existing workspaces and then checks
   174  // if the currently selected workspace is valid. If not, it will ask
   175  // the user to select a workspace from the list.
   176  func (m *Meta) selectWorkspace(b backend.Backend) error {
   177  	workspaces, err := b.Workspaces()
   178  	if err == backend.ErrWorkspacesNotSupported {
   179  		return nil
   180  	}
   181  	if err != nil {
   182  		return fmt.Errorf("Failed to get existing workspaces: %s", err)
   183  	}
   184  	if len(workspaces) == 0 {
   185  		return fmt.Errorf(strings.TrimSpace(errBackendNoExistingWorkspaces))
   186  	}
   187  
   188  	// Get the currently selected workspace.
   189  	workspace, err := m.Workspace()
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	// Check if any of the existing workspaces matches the selected
   195  	// workspace and create a numbered list of existing workspaces.
   196  	var list strings.Builder
   197  	for i, w := range workspaces {
   198  		if w == workspace {
   199  			return nil
   200  		}
   201  		fmt.Fprintf(&list, "%d. %s\n", i+1, w)
   202  	}
   203  
   204  	// If the selected workspace doesn't exist, ask the user to select
   205  	// a workspace from the list of existing workspaces.
   206  	v, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
   207  		Id: "select-workspace",
   208  		Query: fmt.Sprintf(
   209  			"\n[reset][bold][yellow]The currently selected workspace (%s) does not exist.[reset]",
   210  			workspace),
   211  		Description: fmt.Sprintf(
   212  			strings.TrimSpace(inputBackendSelectWorkspace), list.String()),
   213  	})
   214  	if err != nil {
   215  		return fmt.Errorf("Failed to select workspace: %s", err)
   216  	}
   217  
   218  	idx, err := strconv.Atoi(v)
   219  	if err != nil || (idx < 1 || idx > len(workspaces)) {
   220  		return fmt.Errorf("Failed to select workspace: input not a valid number")
   221  	}
   222  
   223  	return m.SetWorkspace(workspaces[idx-1])
   224  }
   225  
   226  // BackendForPlan is similar to Backend, but uses backend settings that were
   227  // stored in a plan.
   228  //
   229  // The current workspace name is also stored as part of the plan, and so this
   230  // method will check that it matches the currently-selected workspace name
   231  // and produce error diagnostics if not.
   232  func (m *Meta) BackendForPlan(settings plans.Backend) (backend.Enhanced, tfdiags.Diagnostics) {
   233  	var diags tfdiags.Diagnostics
   234  
   235  	f := backendInit.Backend(settings.Type)
   236  	if f == nil {
   237  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), settings.Type))
   238  		return nil, diags
   239  	}
   240  	b := f()
   241  	log.Printf("[TRACE] Meta.BackendForPlan: instantiated backend of type %T", b)
   242  
   243  	schema := b.ConfigSchema()
   244  	configVal, err := settings.Config.Decode(schema.ImpliedType())
   245  	if err != nil {
   246  		diags = diags.Append(fmt.Errorf("saved backend configuration is invalid: %w", err))
   247  		return nil, diags
   248  	}
   249  
   250  	newVal, validateDiags := b.PrepareConfig(configVal)
   251  	diags = diags.Append(validateDiags)
   252  	if validateDiags.HasErrors() {
   253  		return nil, diags
   254  	}
   255  
   256  	configureDiags := b.Configure(newVal)
   257  	diags = diags.Append(configureDiags)
   258  
   259  	// If the backend supports CLI initialization, do it.
   260  	if cli, ok := b.(backend.CLI); ok {
   261  		cliOpts, err := m.backendCLIOpts()
   262  		if err != nil {
   263  			diags = diags.Append(err)
   264  			return nil, diags
   265  		}
   266  		if err := cli.CLIInit(cliOpts); err != nil {
   267  			diags = diags.Append(fmt.Errorf(
   268  				"Error initializing backend %T: %s\n\n"+
   269  					"This is a bug; please report it to the backend developer",
   270  				b, err,
   271  			))
   272  			return nil, diags
   273  		}
   274  	}
   275  
   276  	// If the result of loading the backend is an enhanced backend,
   277  	// then return that as-is. This works even if b == nil (it will be !ok).
   278  	if enhanced, ok := b.(backend.Enhanced); ok {
   279  		log.Printf("[TRACE] Meta.BackendForPlan: backend %T supports operations", b)
   280  		return enhanced, nil
   281  	}
   282  
   283  	// Otherwise, we'll wrap our state-only remote backend in the local backend
   284  	// to cause any operations to be run locally.
   285  	log.Printf("[TRACE] Meta.Backend: backend %T does not support operations, so wrapping it in a local backend", b)
   286  	cliOpts, err := m.backendCLIOpts()
   287  	if err != nil {
   288  		diags = diags.Append(err)
   289  		return nil, diags
   290  	}
   291  	cliOpts.Validation = false // don't validate here in case config contains file(...) calls where the file doesn't exist
   292  	local := backendLocal.NewWithBackend(b)
   293  	if err := local.CLIInit(cliOpts); err != nil {
   294  		// Local backend should never fail, so this is always a bug.
   295  		panic(err)
   296  	}
   297  
   298  	return local, diags
   299  }
   300  
   301  // backendCLIOpts returns a backend.CLIOpts object that should be passed to
   302  // a backend that supports local CLI operations.
   303  func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) {
   304  	contextOpts, err := m.contextOpts()
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  	return &backend.CLIOpts{
   309  		CLI:                 m.Ui,
   310  		CLIColor:            m.Colorize(),
   311  		Streams:             m.Streams,
   312  		StatePath:           m.statePath,
   313  		StateOutPath:        m.stateOutPath,
   314  		StateBackupPath:     m.backupPath,
   315  		ContextOpts:         contextOpts,
   316  		Input:               m.Input(),
   317  		RunningInAutomation: m.RunningInAutomation,
   318  	}, nil
   319  }
   320  
   321  // Operation initializes a new backend.Operation struct.
   322  //
   323  // This prepares the operation. After calling this, the caller is expected
   324  // to modify fields of the operation such as Sequence to specify what will
   325  // be called.
   326  func (m *Meta) Operation(b backend.Backend) *backend.Operation {
   327  	schema := b.ConfigSchema()
   328  	workspace, err := m.Workspace()
   329  	if err != nil {
   330  		// An invalid workspace error would have been raised when creating the
   331  		// backend, and the caller should have already exited. Seeing the error
   332  		// here first is a bug, so panic.
   333  		panic(fmt.Sprintf("invalid workspace: %s", err))
   334  	}
   335  	planOutBackend, err := m.backendState.ForPlan(schema, workspace)
   336  	if err != nil {
   337  		// Always indicates an implementation error in practice, because
   338  		// errors here indicate invalid encoding of the backend configuration
   339  		// in memory, and we should always have validated that by the time
   340  		// we get here.
   341  		panic(fmt.Sprintf("failed to encode backend configuration for plan: %s", err))
   342  	}
   343  
   344  	stateLocker := clistate.NewNoopLocker()
   345  	if m.stateLock {
   346  		view := views.NewStateLocker(arguments.ViewHuman, m.View)
   347  		stateLocker = clistate.NewLocker(m.stateLockTimeout, view)
   348  	}
   349  
   350  	return &backend.Operation{
   351  		PlanOutBackend: planOutBackend,
   352  		Parallelism:    m.parallelism,
   353  		Targets:        m.targets,
   354  		UIIn:           m.UIInput(),
   355  		UIOut:          m.Ui,
   356  		Workspace:      workspace,
   357  		StateLocker:    stateLocker,
   358  	}
   359  }
   360  
   361  // backendConfig returns the local configuration for the backend
   362  func (m *Meta) backendConfig(opts *BackendOpts) (*configs.Backend, int, tfdiags.Diagnostics) {
   363  	var diags tfdiags.Diagnostics
   364  
   365  	if opts.Config == nil {
   366  		// check if the config was missing, or just not required
   367  		conf, moreDiags := m.loadBackendConfig(".")
   368  		diags = diags.Append(moreDiags)
   369  		if moreDiags.HasErrors() {
   370  			return nil, 0, diags
   371  		}
   372  
   373  		if conf == nil {
   374  			log.Println("[TRACE] Meta.Backend: no config given or present on disk, so returning nil config")
   375  			return nil, 0, nil
   376  		}
   377  
   378  		log.Printf("[TRACE] Meta.Backend: BackendOpts.Config not set, so using settings loaded from %s", conf.DeclRange)
   379  		opts.Config = conf
   380  	}
   381  
   382  	c := opts.Config
   383  
   384  	if c == nil {
   385  		log.Println("[TRACE] Meta.Backend: no explicit backend config, so returning nil config")
   386  		return nil, 0, nil
   387  	}
   388  
   389  	bf := backendInit.Backend(c.Type)
   390  	if bf == nil {
   391  		diags = diags.Append(&hcl.Diagnostic{
   392  			Severity: hcl.DiagError,
   393  			Summary:  "Invalid backend type",
   394  			Detail:   fmt.Sprintf("There is no backend type named %q.", c.Type),
   395  			Subject:  &c.TypeRange,
   396  		})
   397  		return nil, 0, diags
   398  	}
   399  	b := bf()
   400  
   401  	configSchema := b.ConfigSchema()
   402  	configBody := c.Config
   403  	configHash := c.Hash(configSchema)
   404  
   405  	// If we have an override configuration body then we must apply it now.
   406  	if opts.ConfigOverride != nil {
   407  		log.Println("[TRACE] Meta.Backend: merging -backend-config=... CLI overrides into backend configuration")
   408  		configBody = configs.MergeBodies(configBody, opts.ConfigOverride)
   409  	}
   410  
   411  	log.Printf("[TRACE] Meta.Backend: built configuration for %q backend with hash value %d", c.Type, configHash)
   412  
   413  	// We'll shallow-copy configs.Backend here so that we can replace the
   414  	// body without affecting others that hold this reference.
   415  	configCopy := *c
   416  	configCopy.Config = configBody
   417  	return &configCopy, configHash, diags
   418  }
   419  
   420  // backendFromConfig returns the initialized (not configured) backend
   421  // directly from the config/state..
   422  //
   423  // This function handles various edge cases around backend config loading. For
   424  // example: new config changes, backend type changes, etc.
   425  //
   426  // As of the 0.12 release it can no longer migrate from legacy remote state
   427  // to backends, and will instead instruct users to use 0.11 or earlier as
   428  // a stepping-stone to do that migration.
   429  //
   430  // This function may query the user for input unless input is disabled, in
   431  // which case this function will error.
   432  func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
   433  	// Get the local backend configuration.
   434  	c, cHash, diags := m.backendConfig(opts)
   435  	if diags.HasErrors() {
   436  		return nil, diags
   437  	}
   438  
   439  	// ------------------------------------------------------------------------
   440  	// For historical reasons, current backend configuration for a working
   441  	// directory is kept in a *state-like* file, using the legacy state
   442  	// structures in the Terraform package. It is not actually a Terraform
   443  	// state, and so only the "backend" portion of it is actually used.
   444  	//
   445  	// The remainder of this code often confusingly refers to this as a "state",
   446  	// so it's unfortunately important to remember that this is not actually
   447  	// what we _usually_ think of as "state", and is instead a local working
   448  	// directory "backend configuration state" that is never persisted anywhere.
   449  	//
   450  	// Since the "real" state has since moved on to be represented by
   451  	// states.State, we can recognize the special meaning of state that applies
   452  	// to this function and its callees by their continued use of the
   453  	// otherwise-obsolete terraform.State.
   454  	// ------------------------------------------------------------------------
   455  
   456  	// Get the path to where we store a local cache of backend configuration
   457  	// if we're using a remote backend. This may not yet exist which means
   458  	// we haven't used a non-local backend before. That is okay.
   459  	statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
   460  	sMgr := &clistate.LocalState{Path: statePath}
   461  	if err := sMgr.RefreshState(); err != nil {
   462  		diags = diags.Append(fmt.Errorf("Failed to load state: %s", err))
   463  		return nil, diags
   464  	}
   465  
   466  	// Load the state, it must be non-nil for the tests below but can be empty
   467  	s := sMgr.State()
   468  	if s == nil {
   469  		log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
   470  		s = legacy.NewState()
   471  	} else if s.Backend != nil {
   472  		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
   473  	} else {
   474  		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
   475  	}
   476  
   477  	// if we want to force reconfiguration of the backend, we set the backend
   478  	// state to nil on this copy. This will direct us through the correct
   479  	// configuration path in the switch statement below.
   480  	if m.reconfigure {
   481  		s.Backend = nil
   482  	}
   483  
   484  	// Upon return, we want to set the state we're using in-memory so that
   485  	// we can access it for commands.
   486  	m.backendState = nil
   487  	defer func() {
   488  		if s := sMgr.State(); s != nil && !s.Backend.Empty() {
   489  			m.backendState = s.Backend
   490  		}
   491  	}()
   492  
   493  	if !s.Remote.Empty() {
   494  		// Legacy remote state is no longer supported. User must first
   495  		// migrate with Terraform 0.11 or earlier.
   496  		diags = diags.Append(tfdiags.Sourceless(
   497  			tfdiags.Error,
   498  			"Legacy remote state not supported",
   499  			"This working directory is configured for legacy remote state, which is no longer supported from Terraform v0.12 onwards. To migrate this environment, first run \"terraform init\" under a Terraform 0.11 release, and then upgrade Terraform again.",
   500  		))
   501  		return nil, diags
   502  	}
   503  
   504  	// This switch statement covers all the different combinations of
   505  	// configuring new backends, updating previously-configured backends, etc.
   506  	switch {
   507  	// No configuration set at all. Pure local state.
   508  	case c == nil && s.Backend.Empty():
   509  		log.Printf("[TRACE] Meta.Backend: using default local state only (no backend configuration, and no existing initialized backend)")
   510  		return nil, nil
   511  
   512  	// We're unsetting a backend (moving from backend => local)
   513  	case c == nil && !s.Backend.Empty():
   514  		log.Printf("[TRACE] Meta.Backend: previously-initialized %q backend is no longer present in config", s.Backend.Type)
   515  
   516  		initReason := fmt.Sprintf("Unsetting the previously set backend %q", s.Backend.Type)
   517  		if !opts.Init {
   518  			diags = diags.Append(tfdiags.Sourceless(
   519  				tfdiags.Error,
   520  				"Backend initialization required, please run \"terraform init\"",
   521  				fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
   522  			))
   523  			return nil, diags
   524  		}
   525  
   526  		if !m.migrateState {
   527  			diags = diags.Append(migrateOrReconfigDiag)
   528  			return nil, diags
   529  		}
   530  
   531  		return m.backend_c_r_S(c, cHash, sMgr, true)
   532  
   533  	// Configuring a backend for the first time.
   534  	case c != nil && s.Backend.Empty():
   535  		log.Printf("[TRACE] Meta.Backend: moving from default local state only to %q backend", c.Type)
   536  		if !opts.Init {
   537  			initReason := fmt.Sprintf("Initial configuration of the requested backend %q", c.Type)
   538  			diags = diags.Append(tfdiags.Sourceless(
   539  				tfdiags.Error,
   540  				"Backend initialization required, please run \"terraform init\"",
   541  				fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
   542  			))
   543  			return nil, diags
   544  		}
   545  
   546  		return m.backend_C_r_s(c, cHash, sMgr)
   547  
   548  	// Potentially changing a backend configuration
   549  	case c != nil && !s.Backend.Empty():
   550  		// We are not going to migrate if were not initializing and the hashes
   551  		// match indicating that the stored config is valid. If we are
   552  		// initializing, then we also assume the the backend config is OK if
   553  		// the hashes match, as long as we're not providing any new overrides.
   554  		if (uint64(cHash) == s.Backend.Hash) && (!opts.Init || opts.ConfigOverride == nil) {
   555  			log.Printf("[TRACE] Meta.Backend: using already-initialized, unchanged %q backend configuration", c.Type)
   556  			return m.backend_C_r_S_unchanged(c, cHash, sMgr)
   557  		}
   558  
   559  		// If our configuration is the same, then we're just initializing
   560  		// a previously configured remote backend.
   561  		if !m.backendConfigNeedsMigration(c, s.Backend) {
   562  			log.Printf("[TRACE] Meta.Backend: using already-initialized %q backend configuration", c.Type)
   563  			return m.backend_C_r_S_unchanged(c, cHash, sMgr)
   564  		}
   565  		log.Printf("[TRACE] Meta.Backend: backend configuration has changed (from type %q to type %q)", s.Backend.Type, c.Type)
   566  
   567  		initReason := fmt.Sprintf("Backend configuration changed for %q", c.Type)
   568  		if s.Backend.Type != c.Type {
   569  			initReason = fmt.Sprintf("Backend configuration changed from %q to %q", s.Backend.Type, c.Type)
   570  		}
   571  
   572  		if !opts.Init {
   573  			diags = diags.Append(tfdiags.Sourceless(
   574  				tfdiags.Error,
   575  				"Backend initialization required, please run \"terraform init\"",
   576  				fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
   577  			))
   578  			return nil, diags
   579  		}
   580  
   581  		if !m.migrateState {
   582  			diags = diags.Append(migrateOrReconfigDiag)
   583  			return nil, diags
   584  		}
   585  
   586  		log.Printf("[WARN] backend config has changed since last init")
   587  		return m.backend_C_r_S_changed(c, cHash, sMgr, true)
   588  
   589  	default:
   590  		diags = diags.Append(fmt.Errorf(
   591  			"Unhandled backend configuration state. This is a bug. Please\n"+
   592  				"report this error with the following information.\n\n"+
   593  				"Config Nil: %v\n"+
   594  				"Saved Backend Empty: %v\n",
   595  			c == nil, s.Backend.Empty(),
   596  		))
   597  		return nil, diags
   598  	}
   599  }
   600  
   601  // backendFromState returns the initialized (not configured) backend directly
   602  // from the state. This should be used only when a user runs `terraform init
   603  // -backend=false`. This function returns a local backend if there is no state
   604  // or no backend configured.
   605  func (m *Meta) backendFromState() (backend.Backend, tfdiags.Diagnostics) {
   606  	var diags tfdiags.Diagnostics
   607  	// Get the path to where we store a local cache of backend configuration
   608  	// if we're using a remote backend. This may not yet exist which means
   609  	// we haven't used a non-local backend before. That is okay.
   610  	statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
   611  	sMgr := &clistate.LocalState{Path: statePath}
   612  	if err := sMgr.RefreshState(); err != nil {
   613  		diags = diags.Append(fmt.Errorf("Failed to load state: %s", err))
   614  		return nil, diags
   615  	}
   616  	s := sMgr.State()
   617  	if s == nil {
   618  		// no state, so return a local backend
   619  		log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
   620  		return backendLocal.New(), diags
   621  	}
   622  	if s.Backend == nil {
   623  		// s.Backend is nil, so return a local backend
   624  		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
   625  		return backendLocal.New(), diags
   626  	}
   627  	log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
   628  
   629  	//backend init function
   630  	if s.Backend.Type == "" {
   631  		return backendLocal.New(), diags
   632  	}
   633  	f := backendInit.Backend(s.Backend.Type)
   634  	if f == nil {
   635  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
   636  		return nil, diags
   637  	}
   638  	b := f()
   639  
   640  	// The configuration saved in the working directory state file is used
   641  	// in this case, since it will contain any additional values that
   642  	// were provided via -backend-config arguments on terraform init.
   643  	schema := b.ConfigSchema()
   644  	configVal, err := s.Backend.Config(schema)
   645  	if err != nil {
   646  		diags = diags.Append(tfdiags.Sourceless(
   647  			tfdiags.Error,
   648  			"Failed to decode current backend config",
   649  			fmt.Sprintf("The backend configuration created by the most recent run of \"terraform init\" could not be decoded: %s. The configuration may have been initialized by an earlier version that used an incompatible configuration structure. Run \"terraform init -reconfigure\" to force re-initialization of the backend.", err),
   650  		))
   651  		return nil, diags
   652  	}
   653  
   654  	// Validate the config and then configure the backend
   655  	newVal, validDiags := b.PrepareConfig(configVal)
   656  	diags = diags.Append(validDiags)
   657  	if validDiags.HasErrors() {
   658  		return nil, diags
   659  	}
   660  
   661  	configDiags := b.Configure(newVal)
   662  	diags = diags.Append(configDiags)
   663  	if configDiags.HasErrors() {
   664  		return nil, diags
   665  	}
   666  
   667  	return b, diags
   668  }
   669  
   670  //-------------------------------------------------------------------
   671  // Backend Config Scenarios
   672  //
   673  // The functions below cover handling all the various scenarios that
   674  // can exist when loading a backend. They are named in the format of
   675  // "backend_C_R_S" where C, R, S may be upper or lowercase. Lowercase
   676  // means it is false, uppercase means it is true. The full set of eight
   677  // possible cases is handled.
   678  //
   679  // The fields are:
   680  //
   681  //   * C - Backend configuration is set and changed in TF files
   682  //   * R - Legacy remote state is set
   683  //   * S - Backend configuration is set in the state
   684  //
   685  //-------------------------------------------------------------------
   686  
   687  // Unconfiguring a backend (moving from backend => local).
   688  func (m *Meta) backend_c_r_S(c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool) (backend.Backend, tfdiags.Diagnostics) {
   689  	s := sMgr.State()
   690  
   691  	// Get the backend type for output
   692  	backendType := s.Backend.Type
   693  
   694  	m.Ui.Output(fmt.Sprintf(strings.TrimSpace(outputBackendMigrateLocal), s.Backend.Type))
   695  
   696  	// Grab a purely local backend to get the local state if it exists
   697  	localB, diags := m.Backend(&BackendOpts{ForceLocal: true})
   698  	if diags.HasErrors() {
   699  		return nil, diags
   700  	}
   701  
   702  	// Initialize the configured backend
   703  	b, moreDiags := m.backend_C_r_S_unchanged(c, cHash, sMgr)
   704  	diags = diags.Append(moreDiags)
   705  	if moreDiags.HasErrors() {
   706  		return nil, diags
   707  	}
   708  
   709  	// Perform the migration
   710  	err := m.backendMigrateState(&backendMigrateOpts{
   711  		OneType: s.Backend.Type,
   712  		TwoType: "local",
   713  		One:     b,
   714  		Two:     localB,
   715  	})
   716  	if err != nil {
   717  		diags = diags.Append(err)
   718  		return nil, diags
   719  	}
   720  
   721  	// Remove the stored metadata
   722  	s.Backend = nil
   723  	if err := sMgr.WriteState(s); err != nil {
   724  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err))
   725  		return nil, diags
   726  	}
   727  	if err := sMgr.PersistState(); err != nil {
   728  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err))
   729  		return nil, diags
   730  	}
   731  
   732  	if output {
   733  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   734  			"[reset][green]\n\n"+
   735  				strings.TrimSpace(successBackendUnset), backendType)))
   736  	}
   737  
   738  	// Return no backend
   739  	return nil, diags
   740  }
   741  
   742  // Configuring a backend for the first time.
   743  func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
   744  	// Get the backend
   745  	b, configVal, diags := m.backendInitFromConfig(c)
   746  	if diags.HasErrors() {
   747  		return nil, diags
   748  	}
   749  
   750  	// Grab a purely local backend to get the local state if it exists
   751  	localB, localBDiags := m.Backend(&BackendOpts{ForceLocal: true})
   752  	if localBDiags.HasErrors() {
   753  		diags = diags.Append(localBDiags)
   754  		return nil, diags
   755  	}
   756  
   757  	workspaces, err := localB.Workspaces()
   758  	if err != nil {
   759  		diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
   760  		return nil, diags
   761  	}
   762  
   763  	var localStates []statemgr.Full
   764  	for _, workspace := range workspaces {
   765  		localState, err := localB.StateMgr(workspace)
   766  		if err != nil {
   767  			diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
   768  			return nil, diags
   769  		}
   770  		if err := localState.RefreshState(); err != nil {
   771  			diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
   772  			return nil, diags
   773  		}
   774  
   775  		// We only care about non-empty states.
   776  		if localS := localState.State(); !localS.Empty() {
   777  			log.Printf("[TRACE] Meta.Backend: will need to migrate workspace states because of existing %q workspace", workspace)
   778  			localStates = append(localStates, localState)
   779  		} else {
   780  			log.Printf("[TRACE] Meta.Backend: ignoring local %q workspace because its state is empty", workspace)
   781  		}
   782  	}
   783  
   784  	if len(localStates) > 0 {
   785  		// Perform the migration
   786  		err = m.backendMigrateState(&backendMigrateOpts{
   787  			OneType: "local",
   788  			TwoType: c.Type,
   789  			One:     localB,
   790  			Two:     b,
   791  		})
   792  		if err != nil {
   793  			diags = diags.Append(err)
   794  			return nil, diags
   795  		}
   796  
   797  		// we usually remove the local state after migration to prevent
   798  		// confusion, but adding a default local backend block to the config
   799  		// can get us here too. Don't delete our state if the old and new paths
   800  		// are the same.
   801  		erase := true
   802  		if newLocalB, ok := b.(*backendLocal.Local); ok {
   803  			if localB, ok := localB.(*backendLocal.Local); ok {
   804  				if newLocalB.PathsConflictWith(localB) {
   805  					erase = false
   806  					log.Printf("[TRACE] Meta.Backend: both old and new backends share the same local state paths, so not erasing old state")
   807  				}
   808  			}
   809  		}
   810  
   811  		if erase {
   812  			log.Printf("[TRACE] Meta.Backend: removing old state snapshots from old backend")
   813  			for _, localState := range localStates {
   814  				// We always delete the local state, unless that was our new state too.
   815  				if err := localState.WriteState(nil); err != nil {
   816  					diags = diags.Append(fmt.Errorf(errBackendMigrateLocalDelete, err))
   817  					return nil, diags
   818  				}
   819  				if err := localState.PersistState(); err != nil {
   820  					diags = diags.Append(fmt.Errorf(errBackendMigrateLocalDelete, err))
   821  					return nil, diags
   822  				}
   823  			}
   824  		}
   825  	}
   826  
   827  	if m.stateLock {
   828  		view := views.NewStateLocker(arguments.ViewHuman, m.View)
   829  		stateLocker := clistate.NewLocker(m.stateLockTimeout, view)
   830  		if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil {
   831  			diags = diags.Append(fmt.Errorf("Error locking state: %s", err))
   832  			return nil, diags
   833  		}
   834  		defer stateLocker.Unlock()
   835  	}
   836  
   837  	configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType())
   838  	if err != nil {
   839  		diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %s", err))
   840  		return nil, diags
   841  	}
   842  
   843  	// Store the metadata in our saved state location
   844  	s := sMgr.State()
   845  	if s == nil {
   846  		s = legacy.NewState()
   847  	}
   848  	s.Backend = &legacy.BackendState{
   849  		Type:      c.Type,
   850  		ConfigRaw: json.RawMessage(configJSON),
   851  		Hash:      uint64(cHash),
   852  	}
   853  
   854  	if err := sMgr.WriteState(s); err != nil {
   855  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
   856  		return nil, diags
   857  	}
   858  	if err := sMgr.PersistState(); err != nil {
   859  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
   860  		return nil, diags
   861  	}
   862  
   863  	// By now the backend is successfully configured.
   864  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   865  		"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
   866  
   867  	return b, diags
   868  }
   869  
   870  // Changing a previously saved backend.
   871  func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool) (backend.Backend, tfdiags.Diagnostics) {
   872  	if output {
   873  		// Notify the user
   874  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   875  			"[reset]%s\n\n",
   876  			strings.TrimSpace(outputBackendReconfigure))))
   877  	}
   878  
   879  	// Get the old state
   880  	s := sMgr.State()
   881  
   882  	// Get the backend
   883  	b, configVal, diags := m.backendInitFromConfig(c)
   884  	if diags.HasErrors() {
   885  		return nil, diags
   886  	}
   887  
   888  	// no need to confuse the user if the backend types are the same
   889  	if s.Backend.Type != c.Type {
   890  		m.Ui.Output(strings.TrimSpace(fmt.Sprintf(outputBackendMigrateChange, s.Backend.Type, c.Type)))
   891  	}
   892  
   893  	// Grab the existing backend
   894  	oldB, oldBDiags := m.backend_C_r_S_unchanged(c, cHash, sMgr)
   895  	diags = diags.Append(oldBDiags)
   896  	if oldBDiags.HasErrors() {
   897  		return nil, diags
   898  	}
   899  
   900  	// Perform the migration
   901  	err := m.backendMigrateState(&backendMigrateOpts{
   902  		OneType: s.Backend.Type,
   903  		TwoType: c.Type,
   904  		One:     oldB,
   905  		Two:     b,
   906  	})
   907  	if err != nil {
   908  		diags = diags.Append(err)
   909  		return nil, diags
   910  	}
   911  
   912  	if m.stateLock {
   913  		view := views.NewStateLocker(arguments.ViewHuman, m.View)
   914  		stateLocker := clistate.NewLocker(m.stateLockTimeout, view)
   915  		if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil {
   916  			diags = diags.Append(fmt.Errorf("Error locking state: %s", err))
   917  			return nil, diags
   918  		}
   919  		defer stateLocker.Unlock()
   920  	}
   921  
   922  	configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType())
   923  	if err != nil {
   924  		diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %s", err))
   925  		return nil, diags
   926  	}
   927  
   928  	// Update the backend state
   929  	s = sMgr.State()
   930  	if s == nil {
   931  		s = legacy.NewState()
   932  	}
   933  	s.Backend = &legacy.BackendState{
   934  		Type:      c.Type,
   935  		ConfigRaw: json.RawMessage(configJSON),
   936  		Hash:      uint64(cHash),
   937  	}
   938  
   939  	if err := sMgr.WriteState(s); err != nil {
   940  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
   941  		return nil, diags
   942  	}
   943  	if err := sMgr.PersistState(); err != nil {
   944  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
   945  		return nil, diags
   946  	}
   947  
   948  	if output {
   949  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   950  			"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
   951  	}
   952  
   953  	return b, diags
   954  }
   955  
   956  // Initiailizing an unchanged saved backend
   957  func (m *Meta) backend_C_r_S_unchanged(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
   958  	var diags tfdiags.Diagnostics
   959  
   960  	s := sMgr.State()
   961  
   962  	// it's possible for a backend to be unchanged, and the config itself to
   963  	// have changed by moving a parameter from the config to `-backend-config`
   964  	// In this case we only need to update the Hash.
   965  	if c != nil && s.Backend.Hash != uint64(cHash) {
   966  		s.Backend.Hash = uint64(cHash)
   967  		if err := sMgr.WriteState(s); err != nil {
   968  			diags = diags.Append(err)
   969  			return nil, diags
   970  		}
   971  	}
   972  
   973  	// Get the backend
   974  	f := backendInit.Backend(s.Backend.Type)
   975  	if f == nil {
   976  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
   977  		return nil, diags
   978  	}
   979  	b := f()
   980  
   981  	// The configuration saved in the working directory state file is used
   982  	// in this case, since it will contain any additional values that
   983  	// were provided via -backend-config arguments on terraform init.
   984  	schema := b.ConfigSchema()
   985  	configVal, err := s.Backend.Config(schema)
   986  	if err != nil {
   987  		diags = diags.Append(tfdiags.Sourceless(
   988  			tfdiags.Error,
   989  			"Failed to decode current backend config",
   990  			fmt.Sprintf("The backend configuration created by the most recent run of \"terraform init\" could not be decoded: %s. The configuration may have been initialized by an earlier version that used an incompatible configuration structure. Run \"terraform init -reconfigure\" to force re-initialization of the backend.", err),
   991  		))
   992  		return nil, diags
   993  	}
   994  
   995  	// Validate the config and then configure the backend
   996  	newVal, validDiags := b.PrepareConfig(configVal)
   997  	diags = diags.Append(validDiags)
   998  	if validDiags.HasErrors() {
   999  		return nil, diags
  1000  	}
  1001  
  1002  	configDiags := b.Configure(newVal)
  1003  	diags = diags.Append(configDiags)
  1004  	if configDiags.HasErrors() {
  1005  		return nil, diags
  1006  	}
  1007  
  1008  	return b, diags
  1009  }
  1010  
  1011  //-------------------------------------------------------------------
  1012  // Reusable helper functions for backend management
  1013  //-------------------------------------------------------------------
  1014  
  1015  // backendConfigNeedsMigration returns true if migration might be required to
  1016  // move from the configured backend to the given cached backend config.
  1017  //
  1018  // This must be called with the synthetic *configs.Backend that results from
  1019  // merging in any command-line options for correct behavior.
  1020  //
  1021  // If either the given configuration or cached configuration are invalid then
  1022  // this function will conservatively assume that migration is required,
  1023  // expecting that the migration code will subsequently deal with the same
  1024  // errors.
  1025  func (m *Meta) backendConfigNeedsMigration(c *configs.Backend, s *legacy.BackendState) bool {
  1026  	if s == nil || s.Empty() {
  1027  		log.Print("[TRACE] backendConfigNeedsMigration: no cached config, so migration is required")
  1028  		return true
  1029  	}
  1030  	if c.Type != s.Type {
  1031  		log.Printf("[TRACE] backendConfigNeedsMigration: type changed from %q to %q, so migration is required", s.Type, c.Type)
  1032  		return true
  1033  	}
  1034  
  1035  	// We need the backend's schema to do our comparison here.
  1036  	f := backendInit.Backend(c.Type)
  1037  	if f == nil {
  1038  		log.Printf("[TRACE] backendConfigNeedsMigration: no backend of type %q, which migration codepath must handle", c.Type)
  1039  		return true // let the migration codepath deal with the missing backend
  1040  	}
  1041  	b := f()
  1042  
  1043  	schema := b.ConfigSchema()
  1044  	decSpec := schema.NoneRequired().DecoderSpec()
  1045  	givenVal, diags := hcldec.Decode(c.Config, decSpec, nil)
  1046  	if diags.HasErrors() {
  1047  		log.Printf("[TRACE] backendConfigNeedsMigration: failed to decode given config; migration codepath must handle problem: %s", diags.Error())
  1048  		return true // let the migration codepath deal with these errors
  1049  	}
  1050  
  1051  	cachedVal, err := s.Config(schema)
  1052  	if err != nil {
  1053  		log.Printf("[TRACE] backendConfigNeedsMigration: failed to decode cached config; migration codepath must handle problem: %s", err)
  1054  		return true // let the migration codepath deal with the error
  1055  	}
  1056  
  1057  	// If we get all the way down here then it's the exact equality of the
  1058  	// two decoded values that decides our outcome. It's safe to use RawEquals
  1059  	// here (rather than Equals) because we know that unknown values can
  1060  	// never appear in backend configurations.
  1061  	if cachedVal.RawEquals(givenVal) {
  1062  		log.Print("[TRACE] backendConfigNeedsMigration: given configuration matches cached configuration, so no migration is required")
  1063  		return false
  1064  	}
  1065  	log.Print("[TRACE] backendConfigNeedsMigration: configuration values have changed, so migration is required")
  1066  	return true
  1067  }
  1068  
  1069  func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.Value, tfdiags.Diagnostics) {
  1070  	var diags tfdiags.Diagnostics
  1071  
  1072  	// Get the backend
  1073  	f := backendInit.Backend(c.Type)
  1074  	if f == nil {
  1075  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendNewUnknown), c.Type))
  1076  		return nil, cty.NilVal, diags
  1077  	}
  1078  	b := f()
  1079  
  1080  	schema := b.ConfigSchema()
  1081  	decSpec := schema.NoneRequired().DecoderSpec()
  1082  	configVal, hclDiags := hcldec.Decode(c.Config, decSpec, nil)
  1083  	diags = diags.Append(hclDiags)
  1084  	if hclDiags.HasErrors() {
  1085  		return nil, cty.NilVal, diags
  1086  	}
  1087  
  1088  	// TODO: test
  1089  	if m.Input() {
  1090  		var err error
  1091  		configVal, err = m.inputForSchema(configVal, schema)
  1092  		if err != nil {
  1093  			diags = diags.Append(fmt.Errorf("Error asking for input to configure backend %q: %s", c.Type, err))
  1094  		}
  1095  
  1096  		// We get an unknown here if the if the user aborted input, but we can't
  1097  		// turn that into a config value, so set it to null and let the provider
  1098  		// handle it in PrepareConfig.
  1099  		if !configVal.IsKnown() {
  1100  			configVal = cty.NullVal(configVal.Type())
  1101  		}
  1102  	}
  1103  
  1104  	newVal, validateDiags := b.PrepareConfig(configVal)
  1105  	diags = diags.Append(validateDiags.InConfigBody(c.Config, ""))
  1106  	if validateDiags.HasErrors() {
  1107  		return nil, cty.NilVal, diags
  1108  	}
  1109  
  1110  	configureDiags := b.Configure(newVal)
  1111  	diags = diags.Append(configureDiags.InConfigBody(c.Config, ""))
  1112  
  1113  	return b, configVal, diags
  1114  }
  1115  
  1116  // Helper method to ignore remote backend version conflicts. Only call this
  1117  // for commands which cannot accidentally upgrade remote state files.
  1118  func (m *Meta) ignoreRemoteBackendVersionConflict(b backend.Backend) {
  1119  	if rb, ok := b.(*remoteBackend.Remote); ok {
  1120  		rb.IgnoreVersionConflict()
  1121  	}
  1122  }
  1123  
  1124  // Helper method to check the local Terraform version against the configured
  1125  // version in the remote workspace, returning diagnostics if they conflict.
  1126  func (m *Meta) remoteBackendVersionCheck(b backend.Backend, workspace string) tfdiags.Diagnostics {
  1127  	var diags tfdiags.Diagnostics
  1128  
  1129  	if rb, ok := b.(*remoteBackend.Remote); ok {
  1130  		// Allow user override based on command-line flag
  1131  		if m.ignoreRemoteVersion {
  1132  			rb.IgnoreVersionConflict()
  1133  		}
  1134  		// If the override is set, this check will return a warning instead of
  1135  		// an error
  1136  		versionDiags := rb.VerifyWorkspaceTerraformVersion(workspace)
  1137  		diags = diags.Append(versionDiags)
  1138  		// If there are no errors resulting from this check, we do not need to
  1139  		// check again
  1140  		if !diags.HasErrors() {
  1141  			rb.IgnoreVersionConflict()
  1142  		}
  1143  	}
  1144  
  1145  	return diags
  1146  }
  1147  
  1148  //-------------------------------------------------------------------
  1149  // Output constants and initialization code
  1150  //-------------------------------------------------------------------
  1151  
  1152  const errBackendLocalRead = `
  1153  Error reading local state: %s
  1154  
  1155  Terraform is trying to read your local state to determine if there is
  1156  state to migrate to your newly configured backend. Terraform can't continue
  1157  without this check because that would risk losing state. Please resolve the
  1158  error above and try again.
  1159  `
  1160  
  1161  const errBackendMigrateLocalDelete = `
  1162  Error deleting local state after migration: %s
  1163  
  1164  Your local state is deleted after successfully migrating it to the newly
  1165  configured backend. As part of the deletion process, a backup is made at
  1166  the standard backup path unless explicitly asked not to. To cleanly operate
  1167  with a backend, we must delete the local state file. Please resolve the
  1168  issue above and retry the command.
  1169  `
  1170  
  1171  const errBackendNewUnknown = `
  1172  The backend %q could not be found.
  1173  
  1174  This is the backend specified in your Terraform configuration file.
  1175  This error could be a simple typo in your configuration, but it can also
  1176  be caused by using a Terraform version that doesn't support the specified
  1177  backend type. Please check your configuration and your Terraform version.
  1178  
  1179  If you'd like to run Terraform and store state locally, you can fix this
  1180  error by removing the backend configuration from your configuration.
  1181  `
  1182  
  1183  const errBackendNoExistingWorkspaces = `
  1184  No existing workspaces.
  1185  
  1186  Use the "terraform workspace" command to create and select a new workspace.
  1187  If the backend already contains existing workspaces, you may need to update
  1188  the backend configuration.
  1189  `
  1190  
  1191  const errBackendSavedUnknown = `
  1192  The backend %q could not be found.
  1193  
  1194  This is the backend that this Terraform environment is configured to use
  1195  both in your configuration and saved locally as your last-used backend.
  1196  If it isn't found, it could mean an alternate version of Terraform was
  1197  used with this configuration. Please use the proper version of Terraform that
  1198  contains support for this backend.
  1199  
  1200  If you'd like to force remove this backend, you must update your configuration
  1201  to not use the backend and run "terraform init" (or any other command) again.
  1202  `
  1203  
  1204  const errBackendClearSaved = `
  1205  Error clearing the backend configuration: %s
  1206  
  1207  Terraform removes the saved backend configuration when you're removing a
  1208  configured backend. This must be done so future Terraform runs know to not
  1209  use the backend configuration. Please look at the error above, resolve it,
  1210  and try again.
  1211  `
  1212  
  1213  const errBackendInit = `
  1214  Reason: %s
  1215  
  1216  The "backend" is the interface that Terraform uses to store state,
  1217  perform operations, etc. If this message is showing up, it means that the
  1218  Terraform configuration you're using is using a custom configuration for
  1219  the Terraform backend.
  1220  
  1221  Changes to backend configurations require reinitialization. This allows
  1222  Terraform to set up the new configuration, copy existing state, etc. Please run
  1223  "terraform init" with either the "-reconfigure" or "-migrate-state" flags to
  1224  use the current configuration.
  1225  
  1226  If the change reason above is incorrect, please verify your configuration
  1227  hasn't changed and try again. At this point, no changes to your existing
  1228  configuration or state have been made.
  1229  `
  1230  
  1231  const errBackendWriteSaved = `
  1232  Error saving the backend configuration: %s
  1233  
  1234  Terraform saves the complete backend configuration in a local file for
  1235  configuring the backend on future operations. This cannot be disabled. Errors
  1236  are usually due to simple file permission errors. Please look at the error
  1237  above, resolve it, and try again.
  1238  `
  1239  
  1240  const outputBackendMigrateChange = `
  1241  Terraform detected that the backend type changed from %q to %q.
  1242  `
  1243  
  1244  const outputBackendMigrateLocal = `
  1245  Terraform has detected you're unconfiguring your previously set %q backend.
  1246  `
  1247  
  1248  const outputBackendReconfigure = `
  1249  [reset][bold]Backend configuration changed![reset]
  1250  
  1251  Terraform has detected that the configuration specified for the backend
  1252  has changed. Terraform will now check for existing state in the backends.
  1253  `
  1254  
  1255  const successBackendUnset = `
  1256  Successfully unset the backend %q. Terraform will now operate locally.
  1257  `
  1258  
  1259  const successBackendSet = `
  1260  Successfully configured the backend %q! Terraform will automatically
  1261  use this backend unless the backend configuration changes.
  1262  `
  1263  
  1264  var migrateOrReconfigDiag = tfdiags.Sourceless(
  1265  	tfdiags.Error,
  1266  	"Backend configuration changed",
  1267  	"A change in the backend configuration has been detected, which may require migrating existing state.\n\n"+
  1268  		"If you wish to attempt automatic migration of the state, use \"terraform init -migrate-state\".\n"+
  1269  		`If you wish to store the current configuration with no changes to the state, use "terraform init -reconfigure".`)