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