github.com/pulumi/terraform@v1.4.0/pkg/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  	"bytes"
     8  	"context"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"log"
    13  	"path/filepath"
    14  	"strconv"
    15  	"strings"
    16  
    17  	"github.com/hashicorp/hcl/v2"
    18  	"github.com/hashicorp/hcl/v2/hcldec"
    19  	"github.com/pulumi/terraform/pkg/backend"
    20  	"github.com/pulumi/terraform/pkg/cloud"
    21  	"github.com/pulumi/terraform/pkg/command/arguments"
    22  	"github.com/pulumi/terraform/pkg/command/clistate"
    23  	"github.com/pulumi/terraform/pkg/command/views"
    24  	"github.com/pulumi/terraform/pkg/configs"
    25  	"github.com/pulumi/terraform/pkg/plans"
    26  	"github.com/pulumi/terraform/pkg/states/statemgr"
    27  	"github.com/pulumi/terraform/pkg/terraform"
    28  	"github.com/pulumi/terraform/pkg/tfdiags"
    29  	"github.com/zclconf/go-cty/cty"
    30  	ctyjson "github.com/zclconf/go-cty/cty/json"
    31  
    32  	backendInit "github.com/pulumi/terraform/pkg/backend/init"
    33  	backendLocal "github.com/pulumi/terraform/pkg/backend/local"
    34  	legacy "github.com/pulumi/terraform/pkg/legacy/terraform"
    35  )
    36  
    37  // BackendOpts are the options used to initialize a backend.Backend.
    38  type BackendOpts struct {
    39  	// Config is a representation of the backend configuration block given in
    40  	// the root module, or nil if no such block is present.
    41  	Config *configs.Backend
    42  
    43  	// ConfigOverride is an hcl.Body that, if non-nil, will be used with
    44  	// configs.MergeBodies to override the type-specific backend configuration
    45  	// arguments in Config.
    46  	ConfigOverride hcl.Body
    47  
    48  	// Init should be set to true if initialization is allowed. If this is
    49  	// false, then any configuration that requires configuration will show
    50  	// an error asking the user to reinitialize.
    51  	Init bool
    52  
    53  	// ForceLocal will force a purely local backend, including state.
    54  	// You probably don't want to set this.
    55  	ForceLocal bool
    56  
    57  	// ViewType will set console output format for the
    58  	// initialization operation (JSON or human-readable).
    59  	ViewType arguments.ViewType
    60  }
    61  
    62  // BackendWithRemoteTerraformVersion is a shared interface between the 'remote' and 'cloud' backends
    63  // for simplified type checking when calling functions common to those particular backends.
    64  type BackendWithRemoteTerraformVersion interface {
    65  	IgnoreVersionConflict()
    66  	VerifyWorkspaceTerraformVersion(workspace string) tfdiags.Diagnostics
    67  	IsLocalOperations() bool
    68  }
    69  
    70  // Backend initializes and returns the backend for this CLI session.
    71  //
    72  // The backend is used to perform the actual Terraform operations. This
    73  // abstraction enables easily sliding in new Terraform behavior such as
    74  // remote state storage, remote operations, etc. while allowing the CLI
    75  // to remain mostly identical.
    76  //
    77  // This will initialize a new backend for each call, which can carry some
    78  // overhead with it. Please reuse the returned value for optimal behavior.
    79  //
    80  // Only one backend should be used per Meta. This function is stateful
    81  // and is unsafe to create multiple backends used at once. This function
    82  // can be called multiple times with each backend being "live" (usable)
    83  // one at a time.
    84  //
    85  // A side-effect of this method is the population of m.backendState, recording
    86  // the final resolved backend configuration after dealing with overrides from
    87  // the "terraform init" command line, etc.
    88  func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics) {
    89  	var diags tfdiags.Diagnostics
    90  
    91  	// If no opts are set, then initialize
    92  	if opts == nil {
    93  		opts = &BackendOpts{}
    94  	}
    95  
    96  	// Initialize a backend from the config unless we're forcing a purely
    97  	// local operation.
    98  	var b backend.Backend
    99  	if !opts.ForceLocal {
   100  		var backendDiags tfdiags.Diagnostics
   101  		b, backendDiags = m.backendFromConfig(opts)
   102  		diags = diags.Append(backendDiags)
   103  
   104  		if diags.HasErrors() {
   105  			return nil, diags
   106  		}
   107  
   108  		log.Printf("[TRACE] Meta.Backend: instantiated backend of type %T", b)
   109  	}
   110  
   111  	// Set up the CLI opts we pass into backends that support it.
   112  	cliOpts, err := m.backendCLIOpts()
   113  	if err != nil {
   114  		if errs := providerPluginErrors(nil); errors.As(err, &errs) {
   115  			// This is a special type returned by m.providerFactories, which
   116  			// indicates one or more inconsistencies between the dependency
   117  			// lock file and the provider plugins actually available in the
   118  			// local cache directory.
   119  			//
   120  			// If initialization is allowed, we ignore this error, as it may
   121  			// be resolved by the later step where providers are fetched.
   122  			if !opts.Init {
   123  				var buf bytes.Buffer
   124  				for addr, err := range errs {
   125  					fmt.Fprintf(&buf, "\n  - %s: %s", addr, err)
   126  				}
   127  				suggestion := "To download the plugins required for this configuration, run:\n  terraform init"
   128  				if m.RunningInAutomation {
   129  					// Don't mention "terraform init" specifically if we're running in an automation wrapper
   130  					suggestion = "You must install the required plugins before running Terraform operations."
   131  				}
   132  				diags = diags.Append(tfdiags.Sourceless(
   133  					tfdiags.Error,
   134  					"Required plugins are not installed",
   135  					fmt.Sprintf(
   136  						"The installed provider plugins are not consistent with the packages selected in the dependency lock file:%s\n\nTerraform uses external plugins to integrate with a variety of different infrastructure services. %s",
   137  						buf.String(), suggestion,
   138  					),
   139  				))
   140  				return nil, diags
   141  			}
   142  		} else {
   143  			// All other errors just get generic handling.
   144  			diags = diags.Append(err)
   145  			return nil, diags
   146  		}
   147  	}
   148  	cliOpts.Validation = true
   149  
   150  	// If the backend supports CLI initialization, do it.
   151  	if cli, ok := b.(backend.CLI); ok {
   152  		if err := cli.CLIInit(cliOpts); err != nil {
   153  			diags = diags.Append(fmt.Errorf(
   154  				"Error initializing backend %T: %s\n\n"+
   155  					"This is a bug; please report it to the backend developer",
   156  				b, err,
   157  			))
   158  			return nil, diags
   159  		}
   160  	}
   161  
   162  	// If the result of loading the backend is an enhanced backend,
   163  	// then return that as-is. This works even if b == nil (it will be !ok).
   164  	if enhanced, ok := b.(backend.Enhanced); ok {
   165  		log.Printf("[TRACE] Meta.Backend: backend %T supports operations", b)
   166  		return enhanced, nil
   167  	}
   168  
   169  	// We either have a non-enhanced backend or no backend configured at
   170  	// all. In either case, we use local as our enhanced backend and the
   171  	// non-enhanced (if any) as the state backend.
   172  
   173  	if !opts.ForceLocal {
   174  		log.Printf("[TRACE] Meta.Backend: backend %T does not support operations, so wrapping it in a local backend", b)
   175  	}
   176  
   177  	// Build the local backend
   178  	local := backendLocal.NewWithBackend(b)
   179  	if err := local.CLIInit(cliOpts); err != nil {
   180  		// Local backend isn't allowed to fail. It would be a bug.
   181  		panic(err)
   182  	}
   183  
   184  	// If we got here from backendFromConfig returning nil then m.backendState
   185  	// won't be set, since that codepath considers that to be no backend at all,
   186  	// but our caller considers that to be the local backend with no config
   187  	// and so we'll synthesize a backend state so other code doesn't need to
   188  	// care about this special case.
   189  	//
   190  	// FIXME: We should refactor this so that we more directly and explicitly
   191  	// treat the local backend as the default, including in the UI shown to
   192  	// the user, since the local backend should only be used when learning or
   193  	// in exceptional cases and so it's better to help the user learn that
   194  	// by introducing it as a concept.
   195  	if m.backendState == nil {
   196  		// NOTE: This synthetic object is intentionally _not_ retained in the
   197  		// on-disk record of the backend configuration, which was already dealt
   198  		// with inside backendFromConfig, because we still need that codepath
   199  		// to be able to recognize the lack of a config as distinct from
   200  		// explicitly setting local until we do some more refactoring here.
   201  		m.backendState = &legacy.BackendState{
   202  			Type:      "local",
   203  			ConfigRaw: json.RawMessage("{}"),
   204  		}
   205  	}
   206  
   207  	return local, nil
   208  }
   209  
   210  // selectWorkspace gets a list of existing workspaces and then checks
   211  // if the currently selected workspace is valid. If not, it will ask
   212  // the user to select a workspace from the list.
   213  func (m *Meta) selectWorkspace(b backend.Backend) error {
   214  	workspaces, err := b.Workspaces()
   215  	if err == backend.ErrWorkspacesNotSupported {
   216  		return nil
   217  	}
   218  	if err != nil {
   219  		return fmt.Errorf("Failed to get existing workspaces: %s", err)
   220  	}
   221  	if len(workspaces) == 0 {
   222  		if c, ok := b.(*cloud.Cloud); ok && m.input {
   223  			// len is always 1 if using Name; 0 means we're using Tags and there
   224  			// aren't any matching workspaces. Which might be normal and fine, so
   225  			// let's just ask:
   226  			name, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
   227  				Id:          "create-workspace",
   228  				Query:       "\n[reset][bold][yellow]No workspaces found.[reset]",
   229  				Description: fmt.Sprintf(inputCloudInitCreateWorkspace, strings.Join(c.WorkspaceMapping.Tags, ", ")),
   230  			})
   231  			if err != nil {
   232  				return fmt.Errorf("Couldn't create initial workspace: %w", err)
   233  			}
   234  			name = strings.TrimSpace(name)
   235  			if name == "" {
   236  				return fmt.Errorf("Couldn't create initial workspace: no name provided")
   237  			}
   238  			log.Printf("[TRACE] Meta.selectWorkspace: selecting the new TFC workspace requested by the user (%s)", name)
   239  			return m.SetWorkspace(name)
   240  		} else {
   241  			return fmt.Errorf(strings.TrimSpace(errBackendNoExistingWorkspaces))
   242  		}
   243  	}
   244  
   245  	// Get the currently selected workspace.
   246  	workspace, err := m.Workspace()
   247  	if err != nil {
   248  		return err
   249  	}
   250  
   251  	// Check if any of the existing workspaces matches the selected
   252  	// workspace and create a numbered list of existing workspaces.
   253  	var list strings.Builder
   254  	for i, w := range workspaces {
   255  		if w == workspace {
   256  			log.Printf("[TRACE] Meta.selectWorkspace: the currently selected workspace is present in the configured backend (%s)", workspace)
   257  			return nil
   258  		}
   259  		fmt.Fprintf(&list, "%d. %s\n", i+1, w)
   260  	}
   261  
   262  	// If the backend only has a single workspace, select that as the current workspace
   263  	if len(workspaces) == 1 {
   264  		log.Printf("[TRACE] Meta.selectWorkspace: automatically selecting the single workspace provided by the backend (%s)", workspaces[0])
   265  		return m.SetWorkspace(workspaces[0])
   266  	}
   267  
   268  	if !m.input {
   269  		return fmt.Errorf("Currently selected workspace %q does not exist", workspace)
   270  	}
   271  
   272  	// Otherwise, ask the user to select a workspace from the list of existing workspaces.
   273  	v, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
   274  		Id: "select-workspace",
   275  		Query: fmt.Sprintf(
   276  			"\n[reset][bold][yellow]The currently selected workspace (%s) does not exist.[reset]",
   277  			workspace),
   278  		Description: fmt.Sprintf(
   279  			strings.TrimSpace(inputBackendSelectWorkspace), list.String()),
   280  	})
   281  	if err != nil {
   282  		return fmt.Errorf("Failed to select workspace: %s", err)
   283  	}
   284  
   285  	idx, err := strconv.Atoi(v)
   286  	if err != nil || (idx < 1 || idx > len(workspaces)) {
   287  		return fmt.Errorf("Failed to select workspace: input not a valid number")
   288  	}
   289  
   290  	workspace = workspaces[idx-1]
   291  	log.Printf("[TRACE] Meta.selectWorkspace: setting the current workpace according to user selection (%s)", workspace)
   292  	return m.SetWorkspace(workspace)
   293  }
   294  
   295  // BackendForPlan is similar to Backend, but uses backend settings that were
   296  // stored in a plan.
   297  //
   298  // The current workspace name is also stored as part of the plan, and so this
   299  // method will check that it matches the currently-selected workspace name
   300  // and produce error diagnostics if not.
   301  func (m *Meta) BackendForPlan(settings plans.Backend) (backend.Enhanced, tfdiags.Diagnostics) {
   302  	var diags tfdiags.Diagnostics
   303  
   304  	f := backendInit.Backend(settings.Type)
   305  	if f == nil {
   306  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), settings.Type))
   307  		return nil, diags
   308  	}
   309  	b := f()
   310  	log.Printf("[TRACE] Meta.BackendForPlan: instantiated backend of type %T", b)
   311  
   312  	schema := b.ConfigSchema()
   313  	configVal, err := settings.Config.Decode(schema.ImpliedType())
   314  	if err != nil {
   315  		diags = diags.Append(fmt.Errorf("saved backend configuration is invalid: %w", err))
   316  		return nil, diags
   317  	}
   318  
   319  	newVal, validateDiags := b.PrepareConfig(configVal)
   320  	diags = diags.Append(validateDiags)
   321  	if validateDiags.HasErrors() {
   322  		return nil, diags
   323  	}
   324  
   325  	configureDiags := b.Configure(newVal)
   326  	diags = diags.Append(configureDiags)
   327  	if configureDiags.HasErrors() {
   328  		return nil, diags
   329  	}
   330  
   331  	// If the backend supports CLI initialization, do it.
   332  	if cli, ok := b.(backend.CLI); ok {
   333  		cliOpts, err := m.backendCLIOpts()
   334  		if err != nil {
   335  			diags = diags.Append(err)
   336  			return nil, diags
   337  		}
   338  		if err := cli.CLIInit(cliOpts); err != nil {
   339  			diags = diags.Append(fmt.Errorf(
   340  				"Error initializing backend %T: %s\n\n"+
   341  					"This is a bug; please report it to the backend developer",
   342  				b, err,
   343  			))
   344  			return nil, diags
   345  		}
   346  	}
   347  
   348  	// If the result of loading the backend is an enhanced backend,
   349  	// then return that as-is. This works even if b == nil (it will be !ok).
   350  	if enhanced, ok := b.(backend.Enhanced); ok {
   351  		log.Printf("[TRACE] Meta.BackendForPlan: backend %T supports operations", b)
   352  		return enhanced, nil
   353  	}
   354  
   355  	// Otherwise, we'll wrap our state-only remote backend in the local backend
   356  	// to cause any operations to be run locally.
   357  	log.Printf("[TRACE] Meta.Backend: backend %T does not support operations, so wrapping it in a local backend", b)
   358  	cliOpts, err := m.backendCLIOpts()
   359  	if err != nil {
   360  		diags = diags.Append(err)
   361  		return nil, diags
   362  	}
   363  	cliOpts.Validation = false // don't validate here in case config contains file(...) calls where the file doesn't exist
   364  	local := backendLocal.NewWithBackend(b)
   365  	if err := local.CLIInit(cliOpts); err != nil {
   366  		// Local backend should never fail, so this is always a bug.
   367  		panic(err)
   368  	}
   369  
   370  	return local, diags
   371  }
   372  
   373  // backendCLIOpts returns a backend.CLIOpts object that should be passed to
   374  // a backend that supports local CLI operations.
   375  func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) {
   376  	contextOpts, err := m.contextOpts()
   377  	if contextOpts == nil && err != nil {
   378  		return nil, err
   379  	}
   380  	return &backend.CLIOpts{
   381  		CLI:                 m.Ui,
   382  		CLIColor:            m.Colorize(),
   383  		Streams:             m.Streams,
   384  		StatePath:           m.statePath,
   385  		StateOutPath:        m.stateOutPath,
   386  		StateBackupPath:     m.backupPath,
   387  		ContextOpts:         contextOpts,
   388  		Input:               m.Input(),
   389  		RunningInAutomation: m.RunningInAutomation,
   390  	}, err
   391  }
   392  
   393  // Operation initializes a new backend.Operation struct.
   394  //
   395  // This prepares the operation. After calling this, the caller is expected
   396  // to modify fields of the operation such as Sequence to specify what will
   397  // be called.
   398  func (m *Meta) Operation(b backend.Backend, vt arguments.ViewType) *backend.Operation {
   399  	schema := b.ConfigSchema()
   400  	workspace, err := m.Workspace()
   401  	if err != nil {
   402  		// An invalid workspace error would have been raised when creating the
   403  		// backend, and the caller should have already exited. Seeing the error
   404  		// here first is a bug, so panic.
   405  		panic(fmt.Sprintf("invalid workspace: %s", err))
   406  	}
   407  	planOutBackend, err := m.backendState.ForPlan(schema, workspace)
   408  	if err != nil {
   409  		// Always indicates an implementation error in practice, because
   410  		// errors here indicate invalid encoding of the backend configuration
   411  		// in memory, and we should always have validated that by the time
   412  		// we get here.
   413  		panic(fmt.Sprintf("failed to encode backend configuration for plan: %s", err))
   414  	}
   415  
   416  	stateLocker := clistate.NewNoopLocker()
   417  	if m.stateLock {
   418  		view := views.NewStateLocker(vt, m.View)
   419  		stateLocker = clistate.NewLocker(m.stateLockTimeout, view)
   420  	}
   421  
   422  	depLocks, diags := m.lockedDependencies()
   423  	if diags.HasErrors() {
   424  		// We can't actually report errors from here, but m.lockedDependencies
   425  		// should always have been called earlier to prepare the "ContextOpts"
   426  		// for the backend anyway, so we should never actually get here in
   427  		// a real situation. If we do get here then the backend will inevitably
   428  		// fail downstream somwhere if it tries to use the empty depLocks.
   429  		log.Printf("[WARN] Failed to load dependency locks while preparing backend operation (ignored): %s", diags.Err().Error())
   430  	}
   431  
   432  	return &backend.Operation{
   433  		PlanOutBackend:  planOutBackend,
   434  		Targets:         m.targets,
   435  		UIIn:            m.UIInput(),
   436  		UIOut:           m.Ui,
   437  		Workspace:       workspace,
   438  		StateLocker:     stateLocker,
   439  		DependencyLocks: depLocks,
   440  	}
   441  }
   442  
   443  // backendConfig returns the local configuration for the backend
   444  func (m *Meta) backendConfig(opts *BackendOpts) (*configs.Backend, int, tfdiags.Diagnostics) {
   445  	var diags tfdiags.Diagnostics
   446  
   447  	if opts.Config == nil {
   448  		// check if the config was missing, or just not required
   449  		conf, moreDiags := m.loadBackendConfig(".")
   450  		diags = diags.Append(moreDiags)
   451  		if moreDiags.HasErrors() {
   452  			return nil, 0, diags
   453  		}
   454  
   455  		if conf == nil {
   456  			log.Println("[TRACE] Meta.Backend: no config given or present on disk, so returning nil config")
   457  			return nil, 0, nil
   458  		}
   459  
   460  		log.Printf("[TRACE] Meta.Backend: BackendOpts.Config not set, so using settings loaded from %s", conf.DeclRange)
   461  		opts.Config = conf
   462  	}
   463  
   464  	c := opts.Config
   465  
   466  	if c == nil {
   467  		log.Println("[TRACE] Meta.Backend: no explicit backend config, so returning nil config")
   468  		return nil, 0, nil
   469  	}
   470  
   471  	bf := backendInit.Backend(c.Type)
   472  	if bf == nil {
   473  		detail := fmt.Sprintf("There is no backend type named %q.", c.Type)
   474  		if msg, removed := backendInit.RemovedBackends[c.Type]; removed {
   475  			detail = msg
   476  		}
   477  
   478  		diags = diags.Append(&hcl.Diagnostic{
   479  			Severity: hcl.DiagError,
   480  			Summary:  "Invalid backend type",
   481  			Detail:   detail,
   482  			Subject:  &c.TypeRange,
   483  		})
   484  		return nil, 0, diags
   485  	}
   486  	b := bf()
   487  
   488  	configSchema := b.ConfigSchema()
   489  	configBody := c.Config
   490  	configHash := c.Hash(configSchema)
   491  
   492  	// If we have an override configuration body then we must apply it now.
   493  	if opts.ConfigOverride != nil {
   494  		log.Println("[TRACE] Meta.Backend: merging -backend-config=... CLI overrides into backend configuration")
   495  		configBody = configs.MergeBodies(configBody, opts.ConfigOverride)
   496  	}
   497  
   498  	log.Printf("[TRACE] Meta.Backend: built configuration for %q backend with hash value %d", c.Type, configHash)
   499  
   500  	// We'll shallow-copy configs.Backend here so that we can replace the
   501  	// body without affecting others that hold this reference.
   502  	configCopy := *c
   503  	configCopy.Config = configBody
   504  	return &configCopy, configHash, diags
   505  }
   506  
   507  // backendFromConfig returns the initialized (not configured) backend
   508  // directly from the config/state..
   509  //
   510  // This function handles various edge cases around backend config loading. For
   511  // example: new config changes, backend type changes, etc.
   512  //
   513  // As of the 0.12 release it can no longer migrate from legacy remote state
   514  // to backends, and will instead instruct users to use 0.11 or earlier as
   515  // a stepping-stone to do that migration.
   516  //
   517  // This function may query the user for input unless input is disabled, in
   518  // which case this function will error.
   519  func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
   520  	// Get the local backend configuration.
   521  	c, cHash, diags := m.backendConfig(opts)
   522  	if diags.HasErrors() {
   523  		return nil, diags
   524  	}
   525  
   526  	// ------------------------------------------------------------------------
   527  	// For historical reasons, current backend configuration for a working
   528  	// directory is kept in a *state-like* file, using the legacy state
   529  	// structures in the Terraform package. It is not actually a Terraform
   530  	// state, and so only the "backend" portion of it is actually used.
   531  	//
   532  	// The remainder of this code often confusingly refers to this as a "state",
   533  	// so it's unfortunately important to remember that this is not actually
   534  	// what we _usually_ think of as "state", and is instead a local working
   535  	// directory "backend configuration state" that is never persisted anywhere.
   536  	//
   537  	// Since the "real" state has since moved on to be represented by
   538  	// states.State, we can recognize the special meaning of state that applies
   539  	// to this function and its callees by their continued use of the
   540  	// otherwise-obsolete terraform.State.
   541  	// ------------------------------------------------------------------------
   542  
   543  	// Get the path to where we store a local cache of backend configuration
   544  	// if we're using a remote backend. This may not yet exist which means
   545  	// we haven't used a non-local backend before. That is okay.
   546  	statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
   547  	sMgr := &clistate.LocalState{Path: statePath}
   548  	if err := sMgr.RefreshState(); err != nil {
   549  		diags = diags.Append(fmt.Errorf("Failed to load state: %s", err))
   550  		return nil, diags
   551  	}
   552  
   553  	// Load the state, it must be non-nil for the tests below but can be empty
   554  	s := sMgr.State()
   555  	if s == nil {
   556  		log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
   557  		s = legacy.NewState()
   558  	} else if s.Backend != nil {
   559  		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
   560  	} else {
   561  		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
   562  	}
   563  
   564  	// if we want to force reconfiguration of the backend, we set the backend
   565  	// state to nil on this copy. This will direct us through the correct
   566  	// configuration path in the switch statement below.
   567  	if m.reconfigure {
   568  		s.Backend = nil
   569  	}
   570  
   571  	// Upon return, we want to set the state we're using in-memory so that
   572  	// we can access it for commands.
   573  	m.backendState = nil
   574  	defer func() {
   575  		if s := sMgr.State(); s != nil && !s.Backend.Empty() {
   576  			m.backendState = s.Backend
   577  		}
   578  	}()
   579  
   580  	if !s.Remote.Empty() {
   581  		// Legacy remote state is no longer supported. User must first
   582  		// migrate with Terraform 0.11 or earlier.
   583  		diags = diags.Append(tfdiags.Sourceless(
   584  			tfdiags.Error,
   585  			"Legacy remote state not supported",
   586  			"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.",
   587  		))
   588  		return nil, diags
   589  	}
   590  
   591  	// This switch statement covers all the different combinations of
   592  	// configuring new backends, updating previously-configured backends, etc.
   593  	switch {
   594  	// No configuration set at all. Pure local state.
   595  	case c == nil && s.Backend.Empty():
   596  		log.Printf("[TRACE] Meta.Backend: using default local state only (no backend configuration, and no existing initialized backend)")
   597  		return nil, nil
   598  
   599  	// We're unsetting a backend (moving from backend => local)
   600  	case c == nil && !s.Backend.Empty():
   601  		log.Printf("[TRACE] Meta.Backend: previously-initialized %q backend is no longer present in config", s.Backend.Type)
   602  
   603  		initReason := fmt.Sprintf("Unsetting the previously set backend %q", s.Backend.Type)
   604  		if !opts.Init {
   605  			diags = diags.Append(tfdiags.Sourceless(
   606  				tfdiags.Error,
   607  				"Backend initialization required, please run \"terraform init\"",
   608  				fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
   609  			))
   610  			return nil, diags
   611  		}
   612  
   613  		if s.Backend.Type != "cloud" && !m.migrateState {
   614  			diags = diags.Append(migrateOrReconfigDiag)
   615  			return nil, diags
   616  		}
   617  
   618  		return m.backend_c_r_S(c, cHash, sMgr, true, opts)
   619  
   620  	// Configuring a backend for the first time or -reconfigure flag was used
   621  	case c != nil && s.Backend.Empty():
   622  		log.Printf("[TRACE] Meta.Backend: moving from default local state only to %q backend", c.Type)
   623  		if !opts.Init {
   624  			if c.Type == "cloud" {
   625  				initReason := "Initial configuration of Terraform Cloud"
   626  				diags = diags.Append(tfdiags.Sourceless(
   627  					tfdiags.Error,
   628  					"Terraform Cloud initialization required: please run \"terraform init\"",
   629  					fmt.Sprintf(strings.TrimSpace(errBackendInitCloud), initReason),
   630  				))
   631  			} else {
   632  				initReason := fmt.Sprintf("Initial configuration of the requested backend %q", c.Type)
   633  				diags = diags.Append(tfdiags.Sourceless(
   634  					tfdiags.Error,
   635  					"Backend initialization required, please run \"terraform init\"",
   636  					fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
   637  				))
   638  			}
   639  			return nil, diags
   640  		}
   641  		return m.backend_C_r_s(c, cHash, sMgr, opts)
   642  	// Potentially changing a backend configuration
   643  	case c != nil && !s.Backend.Empty():
   644  		// We are not going to migrate if...
   645  		//
   646  		// We're not initializing
   647  		// AND the backend cache hash values match, indicating that the stored config is valid and completely unchanged.
   648  		// AND we're not providing any overrides. An override can mean a change overriding an unchanged backend block (indicated by the hash value).
   649  		if (uint64(cHash) == s.Backend.Hash) && (!opts.Init || opts.ConfigOverride == nil) {
   650  			log.Printf("[TRACE] Meta.Backend: using already-initialized, unchanged %q backend configuration", c.Type)
   651  			savedBackend, diags := m.savedBackend(sMgr)
   652  			// Verify that selected workspace exist. Otherwise prompt user to create one
   653  			if opts.Init && savedBackend != nil {
   654  				if err := m.selectWorkspace(savedBackend); err != nil {
   655  					diags = diags.Append(err)
   656  					return nil, diags
   657  				}
   658  			}
   659  			return savedBackend, diags
   660  		}
   661  
   662  		// If our configuration (the result of both the literal configuration and given
   663  		// -backend-config options) is the same, then we're just initializing a previously
   664  		// configured backend. The literal configuration may differ, however, so while we
   665  		// don't need to migrate, we update the backend cache hash value.
   666  		if !m.backendConfigNeedsMigration(c, s.Backend) {
   667  			log.Printf("[TRACE] Meta.Backend: using already-initialized %q backend configuration", c.Type)
   668  			savedBackend, moreDiags := m.savedBackend(sMgr)
   669  			diags = diags.Append(moreDiags)
   670  			if moreDiags.HasErrors() {
   671  				return nil, diags
   672  			}
   673  
   674  			// It's possible for a backend to be unchanged, and the config itself to
   675  			// have changed by moving a parameter from the config to `-backend-config`
   676  			// In this case, we update the Hash.
   677  			moreDiags = m.updateSavedBackendHash(cHash, sMgr)
   678  			if moreDiags.HasErrors() {
   679  				return nil, diags
   680  			}
   681  			// Verify that selected workspace exist. Otherwise prompt user to create one
   682  			if opts.Init && savedBackend != nil {
   683  				if err := m.selectWorkspace(savedBackend); err != nil {
   684  					diags = diags.Append(err)
   685  					return nil, diags
   686  				}
   687  			}
   688  
   689  			return savedBackend, diags
   690  		}
   691  		log.Printf("[TRACE] Meta.Backend: backend configuration has changed (from type %q to type %q)", s.Backend.Type, c.Type)
   692  
   693  		cloudMode := cloud.DetectConfigChangeType(s.Backend, c, false)
   694  
   695  		if !opts.Init {
   696  			//user ran another cmd that is not init but they are required to initialize because of a potential relevant change to their backend configuration
   697  			initDiag := m.determineInitReason(s.Backend.Type, c.Type, cloudMode)
   698  			diags = diags.Append(initDiag)
   699  			return nil, diags
   700  		}
   701  
   702  		if !cloudMode.InvolvesCloud() && !m.migrateState {
   703  			diags = diags.Append(migrateOrReconfigDiag)
   704  			return nil, diags
   705  		}
   706  
   707  		log.Printf("[WARN] backend config has changed since last init")
   708  		return m.backend_C_r_S_changed(c, cHash, sMgr, true, opts)
   709  
   710  	default:
   711  		diags = diags.Append(fmt.Errorf(
   712  			"Unhandled backend configuration state. This is a bug. Please\n"+
   713  				"report this error with the following information.\n\n"+
   714  				"Config Nil: %v\n"+
   715  				"Saved Backend Empty: %v\n",
   716  			c == nil, s.Backend.Empty(),
   717  		))
   718  		return nil, diags
   719  	}
   720  }
   721  
   722  func (m *Meta) determineInitReason(previousBackendType string, currentBackendType string, cloudMode cloud.ConfigChangeMode) tfdiags.Diagnostics {
   723  	initReason := ""
   724  	switch cloudMode {
   725  	case cloud.ConfigMigrationIn:
   726  		initReason = fmt.Sprintf("Changed from backend %q to Terraform Cloud", previousBackendType)
   727  	case cloud.ConfigMigrationOut:
   728  		initReason = fmt.Sprintf("Changed from Terraform Cloud to backend %q", previousBackendType)
   729  	case cloud.ConfigChangeInPlace:
   730  		initReason = "Terraform Cloud configuration block has changed"
   731  	default:
   732  		switch {
   733  		case previousBackendType != currentBackendType:
   734  			initReason = fmt.Sprintf("Backend type changed from %q to %q", previousBackendType, currentBackendType)
   735  		default:
   736  			initReason = "Backend configuration block has changed"
   737  		}
   738  	}
   739  
   740  	var diags tfdiags.Diagnostics
   741  	switch cloudMode {
   742  	case cloud.ConfigChangeInPlace:
   743  		diags = diags.Append(tfdiags.Sourceless(
   744  			tfdiags.Error,
   745  			"Terraform Cloud initialization required: please run \"terraform init\"",
   746  			fmt.Sprintf(strings.TrimSpace(errBackendInitCloud), initReason),
   747  		))
   748  	case cloud.ConfigMigrationIn:
   749  		diags = diags.Append(tfdiags.Sourceless(
   750  			tfdiags.Error,
   751  			"Terraform Cloud initialization required: please run \"terraform init\"",
   752  			fmt.Sprintf(strings.TrimSpace(errBackendInitCloud), initReason),
   753  		))
   754  	default:
   755  		diags = diags.Append(tfdiags.Sourceless(
   756  			tfdiags.Error,
   757  			"Backend initialization required: please run \"terraform init\"",
   758  			fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
   759  		))
   760  	}
   761  
   762  	return diags
   763  }
   764  
   765  // backendFromState returns the initialized (not configured) backend directly
   766  // from the state. This should be used only when a user runs `terraform init
   767  // -backend=false`. This function returns a local backend if there is no state
   768  // or no backend configured.
   769  func (m *Meta) backendFromState() (backend.Backend, tfdiags.Diagnostics) {
   770  	var diags tfdiags.Diagnostics
   771  	// Get the path to where we store a local cache of backend configuration
   772  	// if we're using a remote backend. This may not yet exist which means
   773  	// we haven't used a non-local backend before. That is okay.
   774  	statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
   775  	sMgr := &clistate.LocalState{Path: statePath}
   776  	if err := sMgr.RefreshState(); err != nil {
   777  		diags = diags.Append(fmt.Errorf("Failed to load state: %s", err))
   778  		return nil, diags
   779  	}
   780  	s := sMgr.State()
   781  	if s == nil {
   782  		// no state, so return a local backend
   783  		log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
   784  		return backendLocal.New(), diags
   785  	}
   786  	if s.Backend == nil {
   787  		// s.Backend is nil, so return a local backend
   788  		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
   789  		return backendLocal.New(), diags
   790  	}
   791  	log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
   792  
   793  	//backend init function
   794  	if s.Backend.Type == "" {
   795  		return backendLocal.New(), diags
   796  	}
   797  	f := backendInit.Backend(s.Backend.Type)
   798  	if f == nil {
   799  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
   800  		return nil, diags
   801  	}
   802  	b := f()
   803  
   804  	// The configuration saved in the working directory state file is used
   805  	// in this case, since it will contain any additional values that
   806  	// were provided via -backend-config arguments on terraform init.
   807  	schema := b.ConfigSchema()
   808  	configVal, err := s.Backend.Config(schema)
   809  	if err != nil {
   810  		diags = diags.Append(tfdiags.Sourceless(
   811  			tfdiags.Error,
   812  			"Failed to decode current backend config",
   813  			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),
   814  		))
   815  		return nil, diags
   816  	}
   817  
   818  	// Validate the config and then configure the backend
   819  	newVal, validDiags := b.PrepareConfig(configVal)
   820  	diags = diags.Append(validDiags)
   821  	if validDiags.HasErrors() {
   822  		return nil, diags
   823  	}
   824  
   825  	configDiags := b.Configure(newVal)
   826  	diags = diags.Append(configDiags)
   827  	if configDiags.HasErrors() {
   828  		return nil, diags
   829  	}
   830  
   831  	return b, diags
   832  }
   833  
   834  //-------------------------------------------------------------------
   835  // Backend Config Scenarios
   836  //
   837  // The functions below cover handling all the various scenarios that
   838  // can exist when loading a backend. They are named in the format of
   839  // "backend_C_R_S" where C, R, S may be upper or lowercase. Lowercase
   840  // means it is false, uppercase means it is true. The full set of eight
   841  // possible cases is handled.
   842  //
   843  // The fields are:
   844  //
   845  //   * C - Backend configuration is set and changed in TF files
   846  //   * R - Legacy remote state is set
   847  //   * S - Backend configuration is set in the state
   848  //
   849  //-------------------------------------------------------------------
   850  
   851  // Unconfiguring a backend (moving from backend => local).
   852  func (m *Meta) backend_c_r_S(
   853  	c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool, opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
   854  
   855  	var diags tfdiags.Diagnostics
   856  
   857  	vt := arguments.ViewJSON
   858  	// Set default viewtype if none was set as the StateLocker needs to know exactly
   859  	// what viewType we want to have.
   860  	if opts == nil || opts.ViewType != vt {
   861  		vt = arguments.ViewHuman
   862  	}
   863  
   864  	s := sMgr.State()
   865  
   866  	cloudMode := cloud.DetectConfigChangeType(s.Backend, c, false)
   867  	diags = diags.Append(m.assertSupportedCloudInitOptions(cloudMode))
   868  	if diags.HasErrors() {
   869  		return nil, diags
   870  	}
   871  
   872  	// Get the backend type for output
   873  	backendType := s.Backend.Type
   874  
   875  	if cloudMode == cloud.ConfigMigrationOut {
   876  		m.Ui.Output("Migrating from Terraform Cloud to local state.")
   877  	} else {
   878  		m.Ui.Output(fmt.Sprintf(strings.TrimSpace(outputBackendMigrateLocal), s.Backend.Type))
   879  	}
   880  
   881  	// Grab a purely local backend to get the local state if it exists
   882  	localB, moreDiags := m.Backend(&BackendOpts{ForceLocal: true, Init: true})
   883  	diags = diags.Append(moreDiags)
   884  	if moreDiags.HasErrors() {
   885  		return nil, diags
   886  	}
   887  
   888  	// Initialize the configured backend
   889  	b, moreDiags := m.savedBackend(sMgr)
   890  	diags = diags.Append(moreDiags)
   891  	if moreDiags.HasErrors() {
   892  		return nil, diags
   893  	}
   894  
   895  	// Perform the migration
   896  	err := m.backendMigrateState(&backendMigrateOpts{
   897  		SourceType:      s.Backend.Type,
   898  		DestinationType: "local",
   899  		Source:          b,
   900  		Destination:     localB,
   901  		ViewType:        vt,
   902  	})
   903  	if err != nil {
   904  		diags = diags.Append(err)
   905  		return nil, diags
   906  	}
   907  
   908  	// Remove the stored metadata
   909  	s.Backend = nil
   910  	if err := sMgr.WriteState(s); err != nil {
   911  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err))
   912  		return nil, diags
   913  	}
   914  	if err := sMgr.PersistState(); err != nil {
   915  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err))
   916  		return nil, diags
   917  	}
   918  
   919  	if output {
   920  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   921  			"[reset][green]\n\n"+
   922  				strings.TrimSpace(successBackendUnset), backendType)))
   923  	}
   924  
   925  	// Return no backend
   926  	return nil, diags
   927  }
   928  
   929  // Configuring a backend for the first time.
   930  func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState, opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
   931  	var diags tfdiags.Diagnostics
   932  
   933  	vt := arguments.ViewJSON
   934  	// Set default viewtype if none was set as the StateLocker needs to know exactly
   935  	// what viewType we want to have.
   936  	if opts == nil || opts.ViewType != vt {
   937  		vt = arguments.ViewHuman
   938  	}
   939  
   940  	// Grab a purely local backend to get the local state if it exists
   941  	localB, localBDiags := m.Backend(&BackendOpts{ForceLocal: true, Init: true})
   942  	if localBDiags.HasErrors() {
   943  		diags = diags.Append(localBDiags)
   944  		return nil, diags
   945  	}
   946  
   947  	workspaces, err := localB.Workspaces()
   948  	if err != nil {
   949  		diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
   950  		return nil, diags
   951  	}
   952  
   953  	var localStates []statemgr.Full
   954  	for _, workspace := range workspaces {
   955  		localState, err := localB.StateMgr(workspace)
   956  		if err != nil {
   957  			diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
   958  			return nil, diags
   959  		}
   960  		if err := localState.RefreshState(); err != nil {
   961  			diags = diags.Append(fmt.Errorf(errBackendLocalRead, err))
   962  			return nil, diags
   963  		}
   964  
   965  		// We only care about non-empty states.
   966  		if localS := localState.State(); !localS.Empty() {
   967  			log.Printf("[TRACE] Meta.Backend: will need to migrate workspace states because of existing %q workspace", workspace)
   968  			localStates = append(localStates, localState)
   969  		} else {
   970  			log.Printf("[TRACE] Meta.Backend: ignoring local %q workspace because its state is empty", workspace)
   971  		}
   972  	}
   973  
   974  	cloudMode := cloud.DetectConfigChangeType(nil, c, len(localStates) > 0)
   975  	diags = diags.Append(m.assertSupportedCloudInitOptions(cloudMode))
   976  	if diags.HasErrors() {
   977  		return nil, diags
   978  	}
   979  
   980  	// Get the backend
   981  	b, configVal, moreDiags := m.backendInitFromConfig(c)
   982  	diags = diags.Append(moreDiags)
   983  	if diags.HasErrors() {
   984  		return nil, diags
   985  	}
   986  
   987  	if len(localStates) > 0 {
   988  		// Perform the migration
   989  		err = m.backendMigrateState(&backendMigrateOpts{
   990  			SourceType:      "local",
   991  			DestinationType: c.Type,
   992  			Source:          localB,
   993  			Destination:     b,
   994  			ViewType:        vt,
   995  		})
   996  		if err != nil {
   997  			diags = diags.Append(err)
   998  			return nil, diags
   999  		}
  1000  
  1001  		// we usually remove the local state after migration to prevent
  1002  		// confusion, but adding a default local backend block to the config
  1003  		// can get us here too. Don't delete our state if the old and new paths
  1004  		// are the same.
  1005  		erase := true
  1006  		if newLocalB, ok := b.(*backendLocal.Local); ok {
  1007  			if localB, ok := localB.(*backendLocal.Local); ok {
  1008  				if newLocalB.PathsConflictWith(localB) {
  1009  					erase = false
  1010  					log.Printf("[TRACE] Meta.Backend: both old and new backends share the same local state paths, so not erasing old state")
  1011  				}
  1012  			}
  1013  		}
  1014  
  1015  		if erase {
  1016  			log.Printf("[TRACE] Meta.Backend: removing old state snapshots from old backend")
  1017  			for _, localState := range localStates {
  1018  				// We always delete the local state, unless that was our new state too.
  1019  				if err := localState.WriteState(nil); err != nil {
  1020  					diags = diags.Append(fmt.Errorf(errBackendMigrateLocalDelete, err))
  1021  					return nil, diags
  1022  				}
  1023  				if err := localState.PersistState(nil); err != nil {
  1024  					diags = diags.Append(fmt.Errorf(errBackendMigrateLocalDelete, err))
  1025  					return nil, diags
  1026  				}
  1027  			}
  1028  		}
  1029  	}
  1030  
  1031  	if m.stateLock {
  1032  		view := views.NewStateLocker(vt, m.View)
  1033  		stateLocker := clistate.NewLocker(m.stateLockTimeout, view)
  1034  		if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil {
  1035  			diags = diags.Append(fmt.Errorf("Error locking state: %s", err))
  1036  			return nil, diags
  1037  		}
  1038  		defer stateLocker.Unlock()
  1039  	}
  1040  
  1041  	configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType())
  1042  	if err != nil {
  1043  		diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %s", err))
  1044  		return nil, diags
  1045  	}
  1046  
  1047  	// Store the metadata in our saved state location
  1048  	s := sMgr.State()
  1049  	if s == nil {
  1050  		s = legacy.NewState()
  1051  	}
  1052  	s.Backend = &legacy.BackendState{
  1053  		Type:      c.Type,
  1054  		ConfigRaw: json.RawMessage(configJSON),
  1055  		Hash:      uint64(cHash),
  1056  	}
  1057  
  1058  	// Verify that selected workspace exists in the backend.
  1059  	if opts.Init && b != nil {
  1060  		err := m.selectWorkspace(b)
  1061  		if err != nil {
  1062  			diags = diags.Append(err)
  1063  
  1064  			// FIXME: A compatibility oddity with the 'remote' backend.
  1065  			// As an awkward legacy UX, when the remote backend is configured and there
  1066  			// are no workspaces, the output to the user saying that there are none and
  1067  			// the user should create one with 'workspace new' takes the form of an
  1068  			// error message - even though it's happy path, expected behavior.
  1069  			//
  1070  			// Therefore, only return nil with errored diags for everything else, and
  1071  			// allow the remote backend to continue and write its configuration to state
  1072  			// even though no workspace is selected.
  1073  			if c.Type != "remote" {
  1074  				return nil, diags
  1075  			}
  1076  		}
  1077  	}
  1078  
  1079  	if err := sMgr.WriteState(s); err != nil {
  1080  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
  1081  		return nil, diags
  1082  	}
  1083  	if err := sMgr.PersistState(); err != nil {
  1084  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
  1085  		return nil, diags
  1086  	}
  1087  
  1088  	// By now the backend is successfully configured.  If using Terraform Cloud, the success
  1089  	// message is handled as part of the final init message
  1090  	if _, ok := b.(*cloud.Cloud); !ok {
  1091  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1092  			"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
  1093  	}
  1094  
  1095  	return b, diags
  1096  }
  1097  
  1098  // Changing a previously saved backend.
  1099  func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool, opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
  1100  	var diags tfdiags.Diagnostics
  1101  
  1102  	vt := arguments.ViewJSON
  1103  	// Set default viewtype if none was set as the StateLocker needs to know exactly
  1104  	// what viewType we want to have.
  1105  	if opts == nil || opts.ViewType != vt {
  1106  		vt = arguments.ViewHuman
  1107  	}
  1108  
  1109  	// Get the old state
  1110  	s := sMgr.State()
  1111  
  1112  	cloudMode := cloud.DetectConfigChangeType(s.Backend, c, false)
  1113  	diags = diags.Append(m.assertSupportedCloudInitOptions(cloudMode))
  1114  	if diags.HasErrors() {
  1115  		return nil, diags
  1116  	}
  1117  
  1118  	if output {
  1119  		// Notify the user
  1120  		switch cloudMode {
  1121  		case cloud.ConfigChangeInPlace:
  1122  			m.Ui.Output("Terraform Cloud configuration has changed.")
  1123  		case cloud.ConfigMigrationIn:
  1124  			m.Ui.Output(fmt.Sprintf("Migrating from backend %q to Terraform Cloud.", s.Backend.Type))
  1125  		case cloud.ConfigMigrationOut:
  1126  			m.Ui.Output(fmt.Sprintf("Migrating from Terraform Cloud to backend %q.", c.Type))
  1127  		default:
  1128  			if s.Backend.Type != c.Type {
  1129  				output := fmt.Sprintf(outputBackendMigrateChange, s.Backend.Type, c.Type)
  1130  				m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1131  					"[reset]%s\n",
  1132  					strings.TrimSpace(output))))
  1133  			} else {
  1134  				m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1135  					"[reset]%s\n",
  1136  					strings.TrimSpace(outputBackendReconfigure))))
  1137  			}
  1138  		}
  1139  	}
  1140  
  1141  	// Get the backend
  1142  	b, configVal, moreDiags := m.backendInitFromConfig(c)
  1143  	diags = diags.Append(moreDiags)
  1144  	if moreDiags.HasErrors() {
  1145  		return nil, diags
  1146  	}
  1147  
  1148  	// If this is a migration into, out of, or irrelevant to Terraform Cloud
  1149  	// mode then we will do state migration here. Otherwise, we just update
  1150  	// the working directory initialization directly, because Terraform Cloud
  1151  	// doesn't have configurable state storage anyway -- we're only changing
  1152  	// which workspaces are relevant to this configuration, not where their
  1153  	// state lives.
  1154  	if cloudMode != cloud.ConfigChangeInPlace {
  1155  		// Grab the existing backend
  1156  		oldB, oldBDiags := m.savedBackend(sMgr)
  1157  		diags = diags.Append(oldBDiags)
  1158  		if oldBDiags.HasErrors() {
  1159  			return nil, diags
  1160  		}
  1161  
  1162  		// Perform the migration
  1163  		err := m.backendMigrateState(&backendMigrateOpts{
  1164  			SourceType:      s.Backend.Type,
  1165  			DestinationType: c.Type,
  1166  			Source:          oldB,
  1167  			Destination:     b,
  1168  			ViewType:        vt,
  1169  		})
  1170  		if err != nil {
  1171  			diags = diags.Append(err)
  1172  			return nil, diags
  1173  		}
  1174  
  1175  		if m.stateLock {
  1176  			view := views.NewStateLocker(vt, m.View)
  1177  			stateLocker := clistate.NewLocker(m.stateLockTimeout, view)
  1178  			if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil {
  1179  				diags = diags.Append(fmt.Errorf("Error locking state: %s", err))
  1180  				return nil, diags
  1181  			}
  1182  			defer stateLocker.Unlock()
  1183  		}
  1184  	}
  1185  
  1186  	configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType())
  1187  	if err != nil {
  1188  		diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %s", err))
  1189  		return nil, diags
  1190  	}
  1191  
  1192  	// Update the backend state
  1193  	s = sMgr.State()
  1194  	if s == nil {
  1195  		s = legacy.NewState()
  1196  	}
  1197  	s.Backend = &legacy.BackendState{
  1198  		Type:      c.Type,
  1199  		ConfigRaw: json.RawMessage(configJSON),
  1200  		Hash:      uint64(cHash),
  1201  	}
  1202  
  1203  	// Verify that selected workspace exist. Otherwise prompt user to create one
  1204  	if opts.Init && b != nil {
  1205  		if err := m.selectWorkspace(b); err != nil {
  1206  			diags = diags.Append(err)
  1207  			return b, diags
  1208  		}
  1209  	}
  1210  
  1211  	if err := sMgr.WriteState(s); err != nil {
  1212  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
  1213  		return nil, diags
  1214  	}
  1215  	if err := sMgr.PersistState(); err != nil {
  1216  		diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
  1217  		return nil, diags
  1218  	}
  1219  
  1220  	if output {
  1221  		// By now the backend is successfully configured.  If using Terraform Cloud, the success
  1222  		// message is handled as part of the final init message
  1223  		if _, ok := b.(*cloud.Cloud); !ok {
  1224  			m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1225  				"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
  1226  		}
  1227  	}
  1228  
  1229  	return b, diags
  1230  }
  1231  
  1232  // Initializing a saved backend from the cache file (legacy state file)
  1233  //
  1234  // TODO: This is extremely similar to Meta.backendFromState() but for legacy reasons this is the
  1235  // function used by the migration APIs within this file. The other handles 'init -backend=false',
  1236  // specifically.
  1237  func (m *Meta) savedBackend(sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
  1238  	var diags tfdiags.Diagnostics
  1239  
  1240  	s := sMgr.State()
  1241  
  1242  	// Get the backend
  1243  	f := backendInit.Backend(s.Backend.Type)
  1244  	if f == nil {
  1245  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
  1246  		return nil, diags
  1247  	}
  1248  	b := f()
  1249  
  1250  	// The configuration saved in the working directory state file is used
  1251  	// in this case, since it will contain any additional values that
  1252  	// were provided via -backend-config arguments on terraform init.
  1253  	schema := b.ConfigSchema()
  1254  	configVal, err := s.Backend.Config(schema)
  1255  	if err != nil {
  1256  		diags = diags.Append(tfdiags.Sourceless(
  1257  			tfdiags.Error,
  1258  			"Failed to decode current backend config",
  1259  			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),
  1260  		))
  1261  		return nil, diags
  1262  	}
  1263  
  1264  	// Validate the config and then configure the backend
  1265  	newVal, validDiags := b.PrepareConfig(configVal)
  1266  	diags = diags.Append(validDiags)
  1267  	if validDiags.HasErrors() {
  1268  		return nil, diags
  1269  	}
  1270  
  1271  	configDiags := b.Configure(newVal)
  1272  	diags = diags.Append(configDiags)
  1273  	if configDiags.HasErrors() {
  1274  		return nil, diags
  1275  	}
  1276  
  1277  	return b, diags
  1278  }
  1279  
  1280  func (m *Meta) updateSavedBackendHash(cHash int, sMgr *clistate.LocalState) tfdiags.Diagnostics {
  1281  	var diags tfdiags.Diagnostics
  1282  
  1283  	s := sMgr.State()
  1284  
  1285  	if s.Backend.Hash != uint64(cHash) {
  1286  		s.Backend.Hash = uint64(cHash)
  1287  		if err := sMgr.WriteState(s); err != nil {
  1288  			diags = diags.Append(err)
  1289  		}
  1290  	}
  1291  
  1292  	return diags
  1293  }
  1294  
  1295  //-------------------------------------------------------------------
  1296  // Reusable helper functions for backend management
  1297  //-------------------------------------------------------------------
  1298  
  1299  // backendConfigNeedsMigration returns true if migration might be required to
  1300  // move from the configured backend to the given cached backend config.
  1301  //
  1302  // This must be called with the synthetic *configs.Backend that results from
  1303  // merging in any command-line options for correct behavior.
  1304  //
  1305  // If either the given configuration or cached configuration are invalid then
  1306  // this function will conservatively assume that migration is required,
  1307  // expecting that the migration code will subsequently deal with the same
  1308  // errors.
  1309  func (m *Meta) backendConfigNeedsMigration(c *configs.Backend, s *legacy.BackendState) bool {
  1310  	if s == nil || s.Empty() {
  1311  		log.Print("[TRACE] backendConfigNeedsMigration: no cached config, so migration is required")
  1312  		return true
  1313  	}
  1314  	if c.Type != s.Type {
  1315  		log.Printf("[TRACE] backendConfigNeedsMigration: type changed from %q to %q, so migration is required", s.Type, c.Type)
  1316  		return true
  1317  	}
  1318  
  1319  	// We need the backend's schema to do our comparison here.
  1320  	f := backendInit.Backend(c.Type)
  1321  	if f == nil {
  1322  		log.Printf("[TRACE] backendConfigNeedsMigration: no backend of type %q, which migration codepath must handle", c.Type)
  1323  		return true // let the migration codepath deal with the missing backend
  1324  	}
  1325  	b := f()
  1326  
  1327  	schema := b.ConfigSchema()
  1328  	decSpec := schema.NoneRequired().DecoderSpec()
  1329  	givenVal, diags := hcldec.Decode(c.Config, decSpec, nil)
  1330  	if diags.HasErrors() {
  1331  		log.Printf("[TRACE] backendConfigNeedsMigration: failed to decode given config; migration codepath must handle problem: %s", diags.Error())
  1332  		return true // let the migration codepath deal with these errors
  1333  	}
  1334  
  1335  	cachedVal, err := s.Config(schema)
  1336  	if err != nil {
  1337  		log.Printf("[TRACE] backendConfigNeedsMigration: failed to decode cached config; migration codepath must handle problem: %s", err)
  1338  		return true // let the migration codepath deal with the error
  1339  	}
  1340  
  1341  	// If we get all the way down here then it's the exact equality of the
  1342  	// two decoded values that decides our outcome. It's safe to use RawEquals
  1343  	// here (rather than Equals) because we know that unknown values can
  1344  	// never appear in backend configurations.
  1345  	if cachedVal.RawEquals(givenVal) {
  1346  		log.Print("[TRACE] backendConfigNeedsMigration: given configuration matches cached configuration, so no migration is required")
  1347  		return false
  1348  	}
  1349  	log.Print("[TRACE] backendConfigNeedsMigration: configuration values have changed, so migration is required")
  1350  	return true
  1351  }
  1352  
  1353  func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.Value, tfdiags.Diagnostics) {
  1354  	var diags tfdiags.Diagnostics
  1355  
  1356  	// Get the backend
  1357  	f := backendInit.Backend(c.Type)
  1358  	if f == nil {
  1359  		diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendNewUnknown), c.Type))
  1360  		return nil, cty.NilVal, diags
  1361  	}
  1362  	b := f()
  1363  
  1364  	schema := b.ConfigSchema()
  1365  	decSpec := schema.NoneRequired().DecoderSpec()
  1366  	configVal, hclDiags := hcldec.Decode(c.Config, decSpec, nil)
  1367  	diags = diags.Append(hclDiags)
  1368  	if hclDiags.HasErrors() {
  1369  		return nil, cty.NilVal, diags
  1370  	}
  1371  
  1372  	// TODO: test
  1373  	if m.Input() {
  1374  		var err error
  1375  		configVal, err = m.inputForSchema(configVal, schema)
  1376  		if err != nil {
  1377  			diags = diags.Append(fmt.Errorf("Error asking for input to configure backend %q: %s", c.Type, err))
  1378  		}
  1379  
  1380  		// We get an unknown here if the if the user aborted input, but we can't
  1381  		// turn that into a config value, so set it to null and let the provider
  1382  		// handle it in PrepareConfig.
  1383  		if !configVal.IsKnown() {
  1384  			configVal = cty.NullVal(configVal.Type())
  1385  		}
  1386  	}
  1387  
  1388  	newVal, validateDiags := b.PrepareConfig(configVal)
  1389  	diags = diags.Append(validateDiags.InConfigBody(c.Config, ""))
  1390  	if validateDiags.HasErrors() {
  1391  		return nil, cty.NilVal, diags
  1392  	}
  1393  
  1394  	configureDiags := b.Configure(newVal)
  1395  	diags = diags.Append(configureDiags.InConfigBody(c.Config, ""))
  1396  
  1397  	return b, configVal, diags
  1398  }
  1399  
  1400  // Helper method to ignore remote/cloud backend version conflicts. Only call this
  1401  // for commands which cannot accidentally upgrade remote state files.
  1402  func (m *Meta) ignoreRemoteVersionConflict(b backend.Backend) {
  1403  	if back, ok := b.(BackendWithRemoteTerraformVersion); ok {
  1404  		back.IgnoreVersionConflict()
  1405  	}
  1406  }
  1407  
  1408  // Helper method to check the local Terraform version against the configured
  1409  // version in the remote workspace, returning diagnostics if they conflict.
  1410  func (m *Meta) remoteVersionCheck(b backend.Backend, workspace string) tfdiags.Diagnostics {
  1411  	var diags tfdiags.Diagnostics
  1412  
  1413  	if back, ok := b.(BackendWithRemoteTerraformVersion); ok {
  1414  		// Allow user override based on command-line flag
  1415  		if m.ignoreRemoteVersion {
  1416  			back.IgnoreVersionConflict()
  1417  		}
  1418  		// If the override is set, this check will return a warning instead of
  1419  		// an error
  1420  		versionDiags := back.VerifyWorkspaceTerraformVersion(workspace)
  1421  		diags = diags.Append(versionDiags)
  1422  		// If there are no errors resulting from this check, we do not need to
  1423  		// check again
  1424  		if !diags.HasErrors() {
  1425  			back.IgnoreVersionConflict()
  1426  		}
  1427  	}
  1428  
  1429  	return diags
  1430  }
  1431  
  1432  // assertSupportedCloudInitOptions returns diagnostics with errors if the
  1433  // init-related command line options (implied inside the Meta receiver)
  1434  // are incompatible with the given cloud configuration change mode.
  1435  func (m *Meta) assertSupportedCloudInitOptions(mode cloud.ConfigChangeMode) tfdiags.Diagnostics {
  1436  	var diags tfdiags.Diagnostics
  1437  	if mode.InvolvesCloud() {
  1438  		log.Printf("[TRACE] Meta.Backend: Terraform Cloud mode initialization type: %s", mode)
  1439  		if m.reconfigure {
  1440  			if mode.IsCloudMigration() {
  1441  				diags = diags.Append(tfdiags.Sourceless(
  1442  					tfdiags.Error,
  1443  					"Invalid command-line option",
  1444  					"The -reconfigure option is unsupported when migrating to Terraform Cloud, because activating Terraform Cloud involves some additional steps.",
  1445  				))
  1446  			} else {
  1447  				diags = diags.Append(tfdiags.Sourceless(
  1448  					tfdiags.Error,
  1449  					"Invalid command-line option",
  1450  					"The -reconfigure option is for in-place reconfiguration of state backends only, and is not needed when changing Terraform Cloud settings.\n\nWhen using Terraform Cloud, initialization automatically activates any new Cloud configuration settings.",
  1451  				))
  1452  			}
  1453  		}
  1454  		if m.migrateState {
  1455  			name := "-migrate-state"
  1456  			if m.forceInitCopy {
  1457  				// -force copy implies -migrate-state in "terraform init",
  1458  				// so m.migrateState is forced to true in this case even if
  1459  				// the user didn't actually specify it. We'll use the other
  1460  				// name here to avoid being confusing, then.
  1461  				name = "-force-copy"
  1462  			}
  1463  			if mode.IsCloudMigration() {
  1464  				diags = diags.Append(tfdiags.Sourceless(
  1465  					tfdiags.Error,
  1466  					"Invalid command-line option",
  1467  					fmt.Sprintf("The %s option is for migration between state backends only, and is not applicable when using Terraform Cloud.\n\nTerraform Cloud migration has additional steps, configured by interactive prompts.", name),
  1468  				))
  1469  			} else {
  1470  				diags = diags.Append(tfdiags.Sourceless(
  1471  					tfdiags.Error,
  1472  					"Invalid command-line option",
  1473  					fmt.Sprintf("The %s option is for migration between state backends only, and is not applicable when using Terraform Cloud.\n\nState storage is handled automatically by Terraform Cloud and so the state storage location is not configurable.", name),
  1474  				))
  1475  			}
  1476  		}
  1477  	}
  1478  	return diags
  1479  }
  1480  
  1481  //-------------------------------------------------------------------
  1482  // Output constants and initialization code
  1483  //-------------------------------------------------------------------
  1484  
  1485  const errBackendLocalRead = `
  1486  Error reading local state: %s
  1487  
  1488  Terraform is trying to read your local state to determine if there is
  1489  state to migrate to your newly configured backend. Terraform can't continue
  1490  without this check because that would risk losing state. Please resolve the
  1491  error above and try again.
  1492  `
  1493  
  1494  const errBackendMigrateLocalDelete = `
  1495  Error deleting local state after migration: %s
  1496  
  1497  Your local state is deleted after successfully migrating it to the newly
  1498  configured backend. As part of the deletion process, a backup is made at
  1499  the standard backup path unless explicitly asked not to. To cleanly operate
  1500  with a backend, we must delete the local state file. Please resolve the
  1501  issue above and retry the command.
  1502  `
  1503  
  1504  const errBackendNewUnknown = `
  1505  The backend %q could not be found.
  1506  
  1507  This is the backend specified in your Terraform configuration file.
  1508  This error could be a simple typo in your configuration, but it can also
  1509  be caused by using a Terraform version that doesn't support the specified
  1510  backend type. Please check your configuration and your Terraform version.
  1511  
  1512  If you'd like to run Terraform and store state locally, you can fix this
  1513  error by removing the backend configuration from your configuration.
  1514  `
  1515  
  1516  const errBackendNoExistingWorkspaces = `
  1517  No existing workspaces.
  1518  
  1519  Use the "terraform workspace" command to create and select a new workspace.
  1520  If the backend already contains existing workspaces, you may need to update
  1521  the backend configuration.
  1522  `
  1523  
  1524  const errBackendSavedUnknown = `
  1525  The backend %q could not be found.
  1526  
  1527  This is the backend that this Terraform environment is configured to use
  1528  both in your configuration and saved locally as your last-used backend.
  1529  If it isn't found, it could mean an alternate version of Terraform was
  1530  used with this configuration. Please use the proper version of Terraform that
  1531  contains support for this backend.
  1532  
  1533  If you'd like to force remove this backend, you must update your configuration
  1534  to not use the backend and run "terraform init" (or any other command) again.
  1535  `
  1536  
  1537  const errBackendClearSaved = `
  1538  Error clearing the backend configuration: %s
  1539  
  1540  Terraform removes the saved backend configuration when you're removing a
  1541  configured backend. This must be done so future Terraform runs know to not
  1542  use the backend configuration. Please look at the error above, resolve it,
  1543  and try again.
  1544  `
  1545  
  1546  const errBackendInit = `
  1547  Reason: %s
  1548  
  1549  The "backend" is the interface that Terraform uses to store state,
  1550  perform operations, etc. If this message is showing up, it means that the
  1551  Terraform configuration you're using is using a custom configuration for
  1552  the Terraform backend.
  1553  
  1554  Changes to backend configurations require reinitialization. This allows
  1555  Terraform to set up the new configuration, copy existing state, etc. Please run
  1556  "terraform init" with either the "-reconfigure" or "-migrate-state" flags to
  1557  use the current configuration.
  1558  
  1559  If the change reason above is incorrect, please verify your configuration
  1560  hasn't changed and try again. At this point, no changes to your existing
  1561  configuration or state have been made.
  1562  `
  1563  
  1564  const errBackendInitCloud = `
  1565  Reason: %s.
  1566  
  1567  Changes to the Terraform Cloud configuration block require reinitialization, to discover any changes to the available workspaces.
  1568  
  1569  To re-initialize, run:
  1570    terraform init
  1571  
  1572  Terraform has not yet made changes to your existing configuration or state.
  1573  `
  1574  
  1575  const errBackendWriteSaved = `
  1576  Error saving the backend configuration: %s
  1577  
  1578  Terraform saves the complete backend configuration in a local file for
  1579  configuring the backend on future operations. This cannot be disabled. Errors
  1580  are usually due to simple file permission errors. Please look at the error
  1581  above, resolve it, and try again.
  1582  `
  1583  
  1584  const outputBackendMigrateChange = `
  1585  Terraform detected that the backend type changed from %q to %q.
  1586  `
  1587  
  1588  const outputBackendMigrateLocal = `
  1589  Terraform has detected you're unconfiguring your previously set %q backend.
  1590  `
  1591  
  1592  const outputBackendReconfigure = `
  1593  [reset][bold]Backend configuration changed![reset]
  1594  
  1595  Terraform has detected that the configuration specified for the backend
  1596  has changed. Terraform will now check for existing state in the backends.
  1597  `
  1598  
  1599  const inputCloudInitCreateWorkspace = `
  1600  There are no workspaces with the configured tags (%s)
  1601  in your Terraform Cloud organization. To finish initializing, Terraform needs at
  1602  least one workspace available.
  1603  
  1604  Terraform can create a properly tagged workspace for you now. Please enter a
  1605  name to create a new Terraform Cloud workspace.
  1606  `
  1607  
  1608  const successBackendUnset = `
  1609  Successfully unset the backend %q. Terraform will now operate locally.
  1610  `
  1611  
  1612  const successBackendSet = `
  1613  Successfully configured the backend %q! Terraform will automatically
  1614  use this backend unless the backend configuration changes.
  1615  `
  1616  
  1617  var migrateOrReconfigDiag = tfdiags.Sourceless(
  1618  	tfdiags.Error,
  1619  	"Backend configuration changed",
  1620  	"A change in the backend configuration has been detected, which may require migrating existing state.\n\n"+
  1621  		"If you wish to attempt automatic migration of the state, use \"terraform init -migrate-state\".\n"+
  1622  		`If you wish to store the current configuration with no changes to the state, use "terraform init -reconfigure".`)