github.com/hooklift/terraform@v0.11.0-beta1.0.20171117000744-6786c1361ffe/command/meta_backend.go (about)

     1  package command
     2  
     3  // This file contains all the Backend-related function calls on Meta,
     4  // exported and private.
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"log"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/hashicorp/go-multierror"
    16  	"github.com/hashicorp/hcl"
    17  	"github.com/hashicorp/terraform/backend"
    18  	"github.com/hashicorp/terraform/command/clistate"
    19  	"github.com/hashicorp/terraform/config"
    20  	"github.com/hashicorp/terraform/state"
    21  	"github.com/hashicorp/terraform/terraform"
    22  	"github.com/mitchellh/mapstructure"
    23  
    24  	backendinit "github.com/hashicorp/terraform/backend/init"
    25  	backendlocal "github.com/hashicorp/terraform/backend/local"
    26  )
    27  
    28  // BackendOpts are the options used to initialize a backend.Backend.
    29  type BackendOpts struct {
    30  	// Module is the root module from which we will extract the terraform and
    31  	// backend configuration.
    32  	Config *config.Config
    33  
    34  	// ConfigFile is a path to a file that contains configuration that
    35  	// is merged directly into the backend configuration when loaded
    36  	// from a file.
    37  	ConfigFile string
    38  
    39  	// ConfigExtra is extra configuration to merge into the backend
    40  	// configuration after the extra file above.
    41  	ConfigExtra map[string]interface{}
    42  
    43  	// Plan is a plan that is being used. If this is set, the backend
    44  	// configuration and output configuration will come from this plan.
    45  	Plan *terraform.Plan
    46  
    47  	// Init should be set to true if initialization is allowed. If this is
    48  	// false, then any configuration that requires configuration will show
    49  	// an error asking the user to reinitialize.
    50  	Init bool
    51  
    52  	// ForceLocal will force a purely local backend, including state.
    53  	// You probably don't want to set this.
    54  	ForceLocal bool
    55  }
    56  
    57  // Backend initializes and returns the backend for this CLI session.
    58  //
    59  // The backend is used to perform the actual Terraform operations. This
    60  // abstraction enables easily sliding in new Terraform behavior such as
    61  // remote state storage, remote operations, etc. while allowing the CLI
    62  // to remain mostly identical.
    63  //
    64  // This will initialize a new backend for each call, which can carry some
    65  // overhead with it. Please reuse the returned value for optimal behavior.
    66  //
    67  // Only one backend should be used per Meta. This function is stateful
    68  // and is unsafe to create multiple backends used at once. This function
    69  // can be called multiple times with each backend being "live" (usable)
    70  // one at a time.
    71  func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) {
    72  	// If no opts are set, then initialize
    73  	if opts == nil {
    74  		opts = &BackendOpts{}
    75  	}
    76  
    77  	// Initialize a backend from the config unless we're forcing a purely
    78  	// local operation.
    79  	var b backend.Backend
    80  	if !opts.ForceLocal {
    81  		var err error
    82  
    83  		// If we have a plan then, we get the the backend from there. Otherwise,
    84  		// the backend comes from the configuration.
    85  		if opts.Plan != nil {
    86  			b, err = m.backendFromPlan(opts)
    87  		} else {
    88  			b, err = m.backendFromConfig(opts)
    89  		}
    90  		if err != nil {
    91  			return nil, err
    92  		}
    93  
    94  		log.Printf("[INFO] command: backend initialized: %T", b)
    95  	}
    96  
    97  	// Setup the CLI opts we pass into backends that support it
    98  	cliOpts := &backend.CLIOpts{
    99  		CLI:                 m.Ui,
   100  		CLIColor:            m.Colorize(),
   101  		StatePath:           m.statePath,
   102  		StateOutPath:        m.stateOutPath,
   103  		StateBackupPath:     m.backupPath,
   104  		ContextOpts:         m.contextOpts(),
   105  		Input:               m.Input(),
   106  		RunningInAutomation: m.RunningInAutomation,
   107  	}
   108  
   109  	// Don't validate if we have a plan.  Validation is normally harmless here,
   110  	// but validation requires interpolation, and `file()` function calls may
   111  	// not have the original files in the current execution context.
   112  	cliOpts.Validation = opts.Plan == nil
   113  
   114  	// If the backend supports CLI initialization, do it.
   115  	if cli, ok := b.(backend.CLI); ok {
   116  		if err := cli.CLIInit(cliOpts); err != nil {
   117  			return nil, fmt.Errorf(
   118  				"Error initializing backend %T: %s\n\n"+
   119  					"This is a bug, please report it to the backend developer",
   120  				b, err)
   121  		}
   122  	}
   123  
   124  	// If the result of loading the backend is an enhanced backend,
   125  	// then return that as-is. This works even if b == nil (it will be !ok).
   126  	if enhanced, ok := b.(backend.Enhanced); ok {
   127  		return enhanced, nil
   128  	}
   129  
   130  	// We either have a non-enhanced backend or no backend configured at
   131  	// all. In either case, we use local as our enhanced backend and the
   132  	// non-enhanced (if any) as the state backend.
   133  
   134  	if !opts.ForceLocal {
   135  		log.Printf("[INFO] command: backend %T is not enhanced, wrapping in local", b)
   136  	}
   137  
   138  	// Build the local backend
   139  	local := &backendlocal.Local{Backend: b}
   140  	if err := local.CLIInit(cliOpts); err != nil {
   141  		// Local backend isn't allowed to fail. It would be a bug.
   142  		panic(err)
   143  	}
   144  
   145  	return local, nil
   146  }
   147  
   148  // IsLocalBackend returns true if the backend is a local backend. We use this
   149  // for some checks that require a remote backend.
   150  func (m *Meta) IsLocalBackend(b backend.Backend) bool {
   151  	// Is it a local backend?
   152  	bLocal, ok := b.(*backendlocal.Local)
   153  
   154  	// If it is, does it not have an alternate state backend?
   155  	if ok {
   156  		ok = bLocal.Backend == nil
   157  	}
   158  
   159  	return ok
   160  }
   161  
   162  // Operation initializes a new backend.Operation struct.
   163  //
   164  // This prepares the operation. After calling this, the caller is expected
   165  // to modify fields of the operation such as Sequence to specify what will
   166  // be called.
   167  func (m *Meta) Operation() *backend.Operation {
   168  	return &backend.Operation{
   169  		PlanOutBackend:   m.backendState,
   170  		Targets:          m.targets,
   171  		UIIn:             m.UIInput(),
   172  		UIOut:            m.Ui,
   173  		Workspace:        m.Workspace(),
   174  		LockState:        m.stateLock,
   175  		StateLockTimeout: m.stateLockTimeout,
   176  	}
   177  }
   178  
   179  // backendConfig returns the local configuration for the backend
   180  func (m *Meta) backendConfig(opts *BackendOpts) (*config.Backend, error) {
   181  	if opts.Config == nil {
   182  		// check if the config was missing, or just not required
   183  		conf, err := m.Config(".")
   184  		if err != nil {
   185  			return nil, err
   186  		}
   187  
   188  		if conf == nil {
   189  			log.Println("[INFO] command: no config, returning nil")
   190  			return nil, nil
   191  		}
   192  
   193  		log.Println("[WARNING] BackendOpts.Config not set, but config found")
   194  		opts.Config = conf
   195  	}
   196  
   197  	c := opts.Config
   198  
   199  	// If there is no Terraform configuration block, no backend config
   200  	if c.Terraform == nil {
   201  		log.Println("[INFO] command: empty terraform config, returning nil")
   202  		return nil, nil
   203  	}
   204  
   205  	// Get the configuration for the backend itself.
   206  	backend := c.Terraform.Backend
   207  	if backend == nil {
   208  		log.Println("[INFO] command: empty backend config, returning nil")
   209  		return nil, nil
   210  	}
   211  
   212  	// If we have a config file set, load that and merge.
   213  	if opts.ConfigFile != "" {
   214  		log.Printf(
   215  			"[DEBUG] command: loading extra backend config from: %s",
   216  			opts.ConfigFile)
   217  		rc, err := m.backendConfigFile(opts.ConfigFile)
   218  		if err != nil {
   219  			return nil, fmt.Errorf(
   220  				"Error loading extra configuration file for backend: %s", err)
   221  		}
   222  
   223  		// Merge in the configuration
   224  		backend.RawConfig = backend.RawConfig.Merge(rc)
   225  	}
   226  
   227  	// If we have extra config values, merge that
   228  	if len(opts.ConfigExtra) > 0 {
   229  		log.Printf(
   230  			"[DEBUG] command: adding extra backend config from CLI")
   231  		rc, err := config.NewRawConfig(opts.ConfigExtra)
   232  		if err != nil {
   233  			return nil, fmt.Errorf(
   234  				"Error adding extra configuration file for backend: %s", err)
   235  		}
   236  
   237  		// Merge in the configuration
   238  		backend.RawConfig = backend.RawConfig.Merge(rc)
   239  	}
   240  
   241  	// Validate the backend early. We have to do this before the normal
   242  	// config validation pass since backend loading happens earlier.
   243  	if errs := backend.Validate(); len(errs) > 0 {
   244  		return nil, multierror.Append(nil, errs...)
   245  	}
   246  
   247  	// Return the configuration which may or may not be set
   248  	return backend, nil
   249  }
   250  
   251  // backendConfigFile loads the extra configuration to merge with the
   252  // backend configuration from an extra file if specified by
   253  // BackendOpts.ConfigFile.
   254  func (m *Meta) backendConfigFile(path string) (*config.RawConfig, error) {
   255  	// Read the file
   256  	d, err := ioutil.ReadFile(path)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	// Parse it
   262  	hclRoot, err := hcl.Parse(string(d))
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  
   267  	// Decode it
   268  	var c map[string]interface{}
   269  	if err := hcl.DecodeObject(&c, hclRoot); err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	return config.NewRawConfig(c)
   274  }
   275  
   276  // backendFromConfig returns the initialized (not configured) backend
   277  // directly from the config/state..
   278  //
   279  // This function handles any edge cases around backend config loading. For
   280  // example: legacy remote state, new config changes, backend type changes,
   281  // etc.
   282  //
   283  // This function may query the user for input unless input is disabled, in
   284  // which case this function will error.
   285  func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) {
   286  	// Get the local backend configuration.
   287  	c, err := m.backendConfig(opts)
   288  	if err != nil {
   289  		return nil, fmt.Errorf("Error loading backend config: %s", err)
   290  	}
   291  
   292  	// cHash defaults to zero unless c is set
   293  	var cHash uint64
   294  	if c != nil {
   295  		// We need to rehash to get the value since we may have merged the
   296  		// config with an extra ConfigFile. We don't do this when merging
   297  		// because we do want the ORIGINAL value on c so that we store
   298  		// that to not detect drift. This is covered in tests.
   299  		cHash = c.Rehash()
   300  	}
   301  
   302  	// Get the path to where we store a local cache of backend configuration
   303  	// if we're using a remote backend. This may not yet exist which means
   304  	// we haven't used a non-local backend before. That is okay.
   305  	statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
   306  	sMgr := &state.LocalState{Path: statePath}
   307  	if err := sMgr.RefreshState(); err != nil {
   308  		return nil, fmt.Errorf("Error loading state: %s", err)
   309  	}
   310  
   311  	// Load the state, it must be non-nil for the tests below but can be empty
   312  	s := sMgr.State()
   313  	if s == nil {
   314  		log.Printf("[DEBUG] command: no data state file found for backend config")
   315  		s = terraform.NewState()
   316  	}
   317  
   318  	// if we want to force reconfiguration of the backend, we set the backend
   319  	// state to nil on this copy. This will direct us through the correct
   320  	// configuration path in the switch statement below.
   321  	if m.reconfigure {
   322  		s.Backend = nil
   323  	}
   324  
   325  	// Upon return, we want to set the state we're using in-memory so that
   326  	// we can access it for commands.
   327  	m.backendState = nil
   328  	defer func() {
   329  		if s := sMgr.State(); s != nil && !s.Backend.Empty() {
   330  			m.backendState = s.Backend
   331  		}
   332  	}()
   333  
   334  	// This giant switch statement covers all eight possible combinations
   335  	// of state settings between: configuring new backends, saved (previously-
   336  	// configured) backends, and legacy remote state.
   337  	switch {
   338  	// No configuration set at all. Pure local state.
   339  	case c == nil && s.Remote.Empty() && s.Backend.Empty():
   340  		return nil, nil
   341  
   342  	// We're unsetting a backend (moving from backend => local)
   343  	case c == nil && s.Remote.Empty() && !s.Backend.Empty():
   344  		if !opts.Init {
   345  			initReason := fmt.Sprintf(
   346  				"Unsetting the previously set backend %q",
   347  				s.Backend.Type)
   348  			m.backendInitRequired(initReason)
   349  			return nil, errBackendInitRequired
   350  		}
   351  
   352  		return m.backend_c_r_S(c, sMgr, true)
   353  
   354  	// We have a legacy remote state configuration but no new backend config
   355  	case c == nil && !s.Remote.Empty() && s.Backend.Empty():
   356  		return m.backend_c_R_s(c, sMgr)
   357  
   358  	// We have a legacy remote state configuration simultaneously with a
   359  	// saved backend configuration while at the same time disabling backend
   360  	// configuration.
   361  	//
   362  	// This is a naturally impossible case: Terraform will never put you
   363  	// in this state, though it is theoretically possible through manual edits
   364  	case c == nil && !s.Remote.Empty() && !s.Backend.Empty():
   365  		if !opts.Init {
   366  			initReason := fmt.Sprintf(
   367  				"Unsetting the previously set backend %q",
   368  				s.Backend.Type)
   369  			m.backendInitRequired(initReason)
   370  			return nil, errBackendInitRequired
   371  		}
   372  
   373  		return m.backend_c_R_S(c, sMgr)
   374  
   375  	// Configuring a backend for the first time.
   376  	case c != nil && s.Remote.Empty() && s.Backend.Empty():
   377  		if !opts.Init {
   378  			initReason := fmt.Sprintf(
   379  				"Initial configuration of the requested backend %q",
   380  				c.Type)
   381  			m.backendInitRequired(initReason)
   382  			return nil, errBackendInitRequired
   383  		}
   384  
   385  		return m.backend_C_r_s(c, sMgr)
   386  
   387  	// Potentially changing a backend configuration
   388  	case c != nil && s.Remote.Empty() && !s.Backend.Empty():
   389  		// If our configuration is the same, then we're just initializing
   390  		// a previously configured remote backend.
   391  		if !s.Backend.Empty() {
   392  			hash := s.Backend.Hash
   393  			// on init we need an updated hash containing any extra options
   394  			// that were added after merging.
   395  			if opts.Init {
   396  				hash = s.Backend.Rehash()
   397  			}
   398  			if hash == cHash {
   399  				return m.backend_C_r_S_unchanged(c, sMgr)
   400  			}
   401  		}
   402  
   403  		if !opts.Init {
   404  			initReason := fmt.Sprintf(
   405  				"Backend configuration changed for %q",
   406  				c.Type)
   407  			m.backendInitRequired(initReason)
   408  			return nil, errBackendInitRequired
   409  		}
   410  
   411  		log.Printf(
   412  			"[WARN] command: backend config change! saved: %d, new: %d",
   413  			s.Backend.Hash, cHash)
   414  		return m.backend_C_r_S_changed(c, sMgr, true)
   415  
   416  	// Configuring a backend for the first time while having legacy
   417  	// remote state. This is very possible if a Terraform user configures
   418  	// a backend prior to ever running Terraform on an old state.
   419  	case c != nil && !s.Remote.Empty() && s.Backend.Empty():
   420  		if !opts.Init {
   421  			initReason := fmt.Sprintf(
   422  				"Initial configuration for backend %q",
   423  				c.Type)
   424  			m.backendInitRequired(initReason)
   425  			return nil, errBackendInitRequired
   426  		}
   427  
   428  		return m.backend_C_R_s(c, sMgr)
   429  
   430  	// Configuring a backend with both a legacy remote state set
   431  	// and a pre-existing backend saved.
   432  	case c != nil && !s.Remote.Empty() && !s.Backend.Empty():
   433  		// If the hashes are the same, we have a legacy remote state with
   434  		// an unchanged stored backend state.
   435  		hash := s.Backend.Hash
   436  		if opts.Init {
   437  			hash = s.Backend.Rehash()
   438  		}
   439  		if hash == cHash {
   440  			if !opts.Init {
   441  				initReason := fmt.Sprintf(
   442  					"Legacy remote state found with configured backend %q",
   443  					c.Type)
   444  				m.backendInitRequired(initReason)
   445  				return nil, errBackendInitRequired
   446  			}
   447  
   448  			return m.backend_C_R_S_unchanged(c, sMgr, true)
   449  		}
   450  
   451  		if !opts.Init {
   452  			initReason := fmt.Sprintf(
   453  				"Reconfiguring the backend %q",
   454  				c.Type)
   455  			m.backendInitRequired(initReason)
   456  			return nil, errBackendInitRequired
   457  		}
   458  
   459  		// We have change in all three
   460  		return m.backend_C_R_S_changed(c, sMgr)
   461  	default:
   462  		// This should be impossible since all state possibilties are
   463  		// tested above, but we need a default case anyways and we should
   464  		// protect against the scenario where a case is somehow removed.
   465  		return nil, fmt.Errorf(
   466  			"Unhandled backend configuration state. This is a bug. Please\n"+
   467  				"report this error with the following information.\n\n"+
   468  				"Config Nil: %v\n"+
   469  				"Saved Backend Empty: %v\n"+
   470  				"Legacy Remote Empty: %v\n",
   471  			c == nil, s.Backend.Empty(), s.Remote.Empty())
   472  	}
   473  }
   474  
   475  // backendFromPlan loads the backend from a given plan file.
   476  func (m *Meta) backendFromPlan(opts *BackendOpts) (backend.Backend, error) {
   477  	// Precondition check
   478  	if opts.Plan == nil {
   479  		panic("plan should not be nil")
   480  	}
   481  
   482  	// We currently don't allow "-state" to be specified.
   483  	if m.statePath != "" {
   484  		return nil, fmt.Errorf(
   485  			"State path cannot be specified with a plan file. The plan itself contains\n" +
   486  				"the state to use. If you wish to change that, please create a new plan\n" +
   487  				"and specify the state path when creating the plan.")
   488  	}
   489  
   490  	planBackend := opts.Plan.Backend
   491  	planState := opts.Plan.State
   492  	if planState == nil {
   493  		// The state can be nil, we just have to make it empty for the logic
   494  		// in this function.
   495  		planState = terraform.NewState()
   496  	}
   497  
   498  	// Validation only for non-local plans
   499  	local := planState.Remote.Empty() && planBackend.Empty()
   500  	if !local {
   501  		// We currently don't allow "-state-out" to be specified.
   502  		if m.stateOutPath != "" {
   503  			return nil, fmt.Errorf(strings.TrimSpace(errBackendPlanStateFlag))
   504  		}
   505  	}
   506  
   507  	/*
   508  		// Determine the path where we'd be writing state
   509  		path := DefaultStateFilename
   510  		if !planState.Remote.Empty() || !planBackend.Empty() {
   511  			path = filepath.Join(m.DataDir(), DefaultStateFilename)
   512  		}
   513  
   514  		// If the path exists, then we need to verify we're writing the same
   515  		// state lineage. If the path doesn't exist that's okay.
   516  		_, err := os.Stat(path)
   517  		if err != nil && !os.IsNotExist(err) {
   518  			return nil, fmt.Errorf("Error checking state destination: %s", err)
   519  		}
   520  		if err == nil {
   521  			// The file exists, we need to read it and compare
   522  			if err := m.backendFromPlan_compareStates(state, path); err != nil {
   523  				return nil, err
   524  			}
   525  		}
   526  	*/
   527  
   528  	// If we have a stateOutPath, we must also specify it as the
   529  	// input path so we can check it properly. We restore it after this
   530  	// function exits.
   531  	original := m.statePath
   532  	m.statePath = m.stateOutPath
   533  	defer func() { m.statePath = original }()
   534  
   535  	var b backend.Backend
   536  	var err error
   537  	switch {
   538  	// No remote state at all, all local
   539  	case planState.Remote.Empty() && planBackend.Empty():
   540  		log.Printf("[INFO] command: initializing local backend from plan (not set)")
   541  
   542  		// Get the local backend
   543  		b, err = m.Backend(&BackendOpts{ForceLocal: true})
   544  
   545  	// New backend configuration set
   546  	case planState.Remote.Empty() && !planBackend.Empty():
   547  		log.Printf(
   548  			"[INFO] command: initializing backend from plan: %s",
   549  			planBackend.Type)
   550  
   551  		b, err = m.backendInitFromSaved(planBackend)
   552  
   553  	// Legacy remote state set
   554  	case !planState.Remote.Empty() && planBackend.Empty():
   555  		log.Printf(
   556  			"[INFO] command: initializing legacy remote backend from plan: %s",
   557  			planState.Remote.Type)
   558  
   559  		// Write our current state to an inmemory state just so that we
   560  		// have it in the format of state.State
   561  		inmem := &state.InmemState{}
   562  		inmem.WriteState(planState)
   563  
   564  		// Get the backend through the normal means of legacy state
   565  		b, err = m.backend_c_R_s(nil, inmem)
   566  
   567  	// Both set, this can't happen in a plan.
   568  	case !planState.Remote.Empty() && !planBackend.Empty():
   569  		return nil, fmt.Errorf(strings.TrimSpace(errBackendPlanBoth))
   570  	}
   571  
   572  	// If we had an error, return that
   573  	if err != nil {
   574  		return nil, err
   575  	}
   576  
   577  	env := m.Workspace()
   578  
   579  	// Get the state so we can determine the effect of using this plan
   580  	realMgr, err := b.State(env)
   581  	if err != nil {
   582  		return nil, fmt.Errorf("Error reading state: %s", err)
   583  	}
   584  
   585  	if m.stateLock {
   586  		lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
   587  		defer cancel()
   588  
   589  		// Lock the state if we can
   590  		lockInfo := state.NewLockInfo()
   591  		lockInfo.Operation = "backend from plan"
   592  
   593  		lockID, err := clistate.Lock(lockCtx, realMgr, lockInfo, m.Ui, m.Colorize())
   594  		if err != nil {
   595  			return nil, fmt.Errorf("Error locking state: %s", err)
   596  		}
   597  		defer clistate.Unlock(realMgr, lockID, m.Ui, m.Colorize())
   598  	}
   599  
   600  	if err := realMgr.RefreshState(); err != nil {
   601  		return nil, fmt.Errorf("Error reading state: %s", err)
   602  	}
   603  	real := realMgr.State()
   604  	if real != nil {
   605  		// If they're not the same lineage, don't allow this
   606  		if !real.SameLineage(planState) {
   607  			return nil, fmt.Errorf(strings.TrimSpace(errBackendPlanLineageDiff))
   608  		}
   609  
   610  		// Compare ages
   611  		comp, err := real.CompareAges(planState)
   612  		if err != nil {
   613  			return nil, fmt.Errorf("Error comparing state ages for safety: %s", err)
   614  		}
   615  		switch comp {
   616  		case terraform.StateAgeEqual:
   617  			// State ages are equal, this is perfect
   618  
   619  		case terraform.StateAgeReceiverOlder:
   620  			// Real state is somehow older, this is okay.
   621  
   622  		case terraform.StateAgeReceiverNewer:
   623  			// If we have an older serial it is a problem but if we have a
   624  			// differing serial but are still identical, just let it through.
   625  			if real.Equal(planState) {
   626  				log.Printf(
   627  					"[WARN] command: state in plan has older serial, but Equal is true")
   628  				break
   629  			}
   630  
   631  			// The real state is newer, this is not allowed.
   632  			return nil, fmt.Errorf(
   633  				strings.TrimSpace(errBackendPlanOlder),
   634  				planState.Serial, real.Serial)
   635  		}
   636  	}
   637  
   638  	// Write the state
   639  	newState := opts.Plan.State.DeepCopy()
   640  	if newState != nil {
   641  		newState.Remote = nil
   642  		newState.Backend = nil
   643  	}
   644  
   645  	// realMgr locked above
   646  	if err := realMgr.WriteState(newState); err != nil {
   647  		return nil, fmt.Errorf("Error writing state: %s", err)
   648  	}
   649  	if err := realMgr.PersistState(); err != nil {
   650  		return nil, fmt.Errorf("Error writing state: %s", err)
   651  	}
   652  
   653  	return b, nil
   654  }
   655  
   656  //-------------------------------------------------------------------
   657  // Backend Config Scenarios
   658  //
   659  // The functions below cover handling all the various scenarios that
   660  // can exist when loading a backend. They are named in the format of
   661  // "backend_C_R_S" where C, R, S may be upper or lowercase. Lowercase
   662  // means it is false, uppercase means it is true. The full set of eight
   663  // possible cases is handled.
   664  //
   665  // The fields are:
   666  //
   667  //   * C - Backend configuration is set and changed in TF files
   668  //   * R - Legacy remote state is set
   669  //   * S - Backend configuration is set in the state
   670  //
   671  //-------------------------------------------------------------------
   672  
   673  // Unconfiguring a backend (moving from backend => local).
   674  func (m *Meta) backend_c_r_S(
   675  	c *config.Backend, sMgr state.State, output bool) (backend.Backend, error) {
   676  	s := sMgr.State()
   677  
   678  	// Get the backend type for output
   679  	backendType := s.Backend.Type
   680  
   681  	copy := m.forceInitCopy
   682  	if !copy {
   683  		var err error
   684  		// Confirm with the user that the copy should occur
   685  		copy, err = m.confirm(&terraform.InputOpts{
   686  			Id:    "backend-migrate-to-local",
   687  			Query: fmt.Sprintf("Do you want to copy the state from %q?", s.Backend.Type),
   688  			Description: fmt.Sprintf(
   689  				strings.TrimSpace(inputBackendMigrateLocal), s.Backend.Type),
   690  		})
   691  		if err != nil {
   692  			return nil, fmt.Errorf(
   693  				"Error asking for state copy action: %s", err)
   694  		}
   695  	}
   696  
   697  	// If we're copying, perform the migration
   698  	if copy {
   699  		// Grab a purely local backend to get the local state if it exists
   700  		localB, err := m.Backend(&BackendOpts{ForceLocal: true})
   701  		if err != nil {
   702  			return nil, fmt.Errorf(strings.TrimSpace(errBackendLocalRead), err)
   703  		}
   704  
   705  		// Initialize the configured backend
   706  		b, err := m.backend_C_r_S_unchanged(c, sMgr)
   707  		if err != nil {
   708  			return nil, fmt.Errorf(
   709  				strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err)
   710  		}
   711  
   712  		// Perform the migration
   713  		err = m.backendMigrateState(&backendMigrateOpts{
   714  			OneType: s.Backend.Type,
   715  			TwoType: "local",
   716  			One:     b,
   717  			Two:     localB,
   718  		})
   719  		if err != nil {
   720  			return nil, err
   721  		}
   722  	}
   723  
   724  	// Remove the stored metadata
   725  	s.Backend = nil
   726  	if err := sMgr.WriteState(s); err != nil {
   727  		return nil, fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err)
   728  	}
   729  	if err := sMgr.PersistState(); err != nil {
   730  		return nil, fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err)
   731  	}
   732  
   733  	if output {
   734  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   735  			"[reset][green]\n\n"+
   736  				strings.TrimSpace(successBackendUnset), backendType)))
   737  	}
   738  
   739  	// Return no backend
   740  	return nil, nil
   741  }
   742  
   743  // Legacy remote state
   744  func (m *Meta) backend_c_R_s(
   745  	c *config.Backend, sMgr state.State) (backend.Backend, error) {
   746  	s := sMgr.State()
   747  
   748  	// Warn the user
   749  	m.Ui.Warn(strings.TrimSpace(warnBackendLegacy) + "\n")
   750  
   751  	// We need to convert the config to map[string]interface{} since that
   752  	// is what the backends expect.
   753  	var configMap map[string]interface{}
   754  	if err := mapstructure.Decode(s.Remote.Config, &configMap); err != nil {
   755  		return nil, fmt.Errorf("Error configuring remote state: %s", err)
   756  	}
   757  
   758  	// Create the config
   759  	rawC, err := config.NewRawConfig(configMap)
   760  	if err != nil {
   761  		return nil, fmt.Errorf("Error configuring remote state: %s", err)
   762  	}
   763  	config := terraform.NewResourceConfig(rawC)
   764  
   765  	// Get the backend
   766  	f := backendinit.Backend(s.Remote.Type)
   767  	if f == nil {
   768  		return nil, fmt.Errorf(strings.TrimSpace(errBackendLegacyUnknown), s.Remote.Type)
   769  	}
   770  	b := f()
   771  
   772  	// Configure
   773  	if err := b.Configure(config); err != nil {
   774  		return nil, fmt.Errorf(errBackendLegacyConfig, err)
   775  	}
   776  
   777  	return b, nil
   778  }
   779  
   780  // Unsetting backend, saved backend, legacy remote state
   781  func (m *Meta) backend_c_R_S(
   782  	c *config.Backend, sMgr state.State) (backend.Backend, error) {
   783  	// Notify the user
   784  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   785  		"[reset]%s\n\n",
   786  		strings.TrimSpace(outputBackendUnsetWithLegacy))))
   787  
   788  	// Get the backend type for later
   789  	backendType := sMgr.State().Backend.Type
   790  
   791  	// First, perform the configured => local tranasition
   792  	if _, err := m.backend_c_r_S(c, sMgr, false); err != nil {
   793  		return nil, err
   794  	}
   795  
   796  	// Grab a purely local backend
   797  	localB, err := m.Backend(&BackendOpts{ForceLocal: true})
   798  	if err != nil {
   799  		return nil, fmt.Errorf(errBackendLocalRead, err)
   800  	}
   801  
   802  	// Grab the state
   803  	s := sMgr.State()
   804  
   805  	// Ask the user if they want to migrate their existing remote state
   806  	copy := m.forceInitCopy
   807  	if !copy {
   808  		copy, err = m.confirm(&terraform.InputOpts{
   809  			Id: "backend-migrate-to-new",
   810  			Query: fmt.Sprintf(
   811  				"Do you want to copy the legacy remote state from %q?",
   812  				s.Remote.Type),
   813  			Description: strings.TrimSpace(inputBackendMigrateLegacyLocal),
   814  		})
   815  		if err != nil {
   816  			return nil, fmt.Errorf(
   817  				"Error asking for state copy action: %s", err)
   818  		}
   819  	}
   820  
   821  	// If the user wants a copy, copy!
   822  	if copy {
   823  		// Initialize the legacy backend
   824  		oldB, err := m.backendInitFromLegacy(s.Remote)
   825  		if err != nil {
   826  			return nil, err
   827  		}
   828  
   829  		// Perform the migration
   830  		err = m.backendMigrateState(&backendMigrateOpts{
   831  			OneType: s.Remote.Type,
   832  			TwoType: "local",
   833  			One:     oldB,
   834  			Two:     localB,
   835  		})
   836  		if err != nil {
   837  			return nil, err
   838  		}
   839  	}
   840  
   841  	// Unset the remote state
   842  	s = sMgr.State()
   843  	if s == nil {
   844  		s = terraform.NewState()
   845  	}
   846  	s.Remote = nil
   847  	if err := sMgr.WriteState(s); err != nil {
   848  		return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err)
   849  	}
   850  	if err := sMgr.PersistState(); err != nil {
   851  		return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err)
   852  	}
   853  
   854  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   855  		"[reset][green]\n\n"+
   856  			strings.TrimSpace(successBackendUnset), backendType)))
   857  
   858  	return nil, nil
   859  }
   860  
   861  // Configuring a backend for the first time with legacy remote state.
   862  func (m *Meta) backend_C_R_s(
   863  	c *config.Backend, sMgr state.State) (backend.Backend, error) {
   864  	// Notify the user
   865  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   866  		"[reset]%s\n\n",
   867  		strings.TrimSpace(outputBackendConfigureWithLegacy))))
   868  
   869  	// First, configure the new backend
   870  	b, err := m.backendInitFromConfig(c)
   871  	if err != nil {
   872  		return nil, err
   873  	}
   874  
   875  	// Next, save the new configuration. This will not overwrite our
   876  	// legacy remote state. We'll handle that after.
   877  	s := sMgr.State()
   878  	if s == nil {
   879  		s = terraform.NewState()
   880  	}
   881  	s.Backend = &terraform.BackendState{
   882  		Type:   c.Type,
   883  		Config: c.RawConfig.Raw,
   884  		Hash:   c.Hash,
   885  	}
   886  	if err := sMgr.WriteState(s); err != nil {
   887  		return nil, fmt.Errorf(errBackendWriteSaved, err)
   888  	}
   889  	if err := sMgr.PersistState(); err != nil {
   890  		return nil, fmt.Errorf(errBackendWriteSaved, err)
   891  	}
   892  
   893  	// I don't know how this is possible but if we don't have remote
   894  	// state config anymore somehow, just return the backend. This
   895  	// shouldn't be possible, though.
   896  	if s.Remote.Empty() {
   897  		return b, nil
   898  	}
   899  
   900  	// Finally, ask the user if they want to copy the state from
   901  	// their old remote state location.
   902  	copy := m.forceInitCopy
   903  	if !copy {
   904  		copy, err = m.confirm(&terraform.InputOpts{
   905  			Id: "backend-migrate-to-new",
   906  			Query: fmt.Sprintf(
   907  				"Do you want to copy the legacy remote state from %q?",
   908  				s.Remote.Type),
   909  			Description: strings.TrimSpace(inputBackendMigrateLegacy),
   910  		})
   911  		if err != nil {
   912  			return nil, fmt.Errorf(
   913  				"Error asking for state copy action: %s", err)
   914  		}
   915  	}
   916  
   917  	// If the user wants a copy, copy!
   918  	if copy {
   919  		// Initialize the legacy backend
   920  		oldB, err := m.backendInitFromLegacy(s.Remote)
   921  		if err != nil {
   922  			return nil, err
   923  		}
   924  
   925  		// Perform the migration
   926  		err = m.backendMigrateState(&backendMigrateOpts{
   927  			OneType: s.Remote.Type,
   928  			TwoType: c.Type,
   929  			One:     oldB,
   930  			Two:     b,
   931  		})
   932  		if err != nil {
   933  			return nil, err
   934  		}
   935  	}
   936  
   937  	// Unset the remote state
   938  	s = sMgr.State()
   939  	if s == nil {
   940  		s = terraform.NewState()
   941  	}
   942  	s.Remote = nil
   943  	if err := sMgr.WriteState(s); err != nil {
   944  		return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err)
   945  	}
   946  	if err := sMgr.PersistState(); err != nil {
   947  		return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err)
   948  	}
   949  
   950  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
   951  		"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
   952  
   953  	return b, nil
   954  }
   955  
   956  // Configuring a backend for the first time.
   957  func (m *Meta) backend_C_r_s(
   958  	c *config.Backend, sMgr state.State) (backend.Backend, error) {
   959  	// Get the backend
   960  	b, err := m.backendInitFromConfig(c)
   961  	if err != nil {
   962  		return nil, err
   963  	}
   964  
   965  	// Grab a purely local backend to get the local state if it exists
   966  	localB, err := m.Backend(&BackendOpts{ForceLocal: true})
   967  	if err != nil {
   968  		return nil, fmt.Errorf(errBackendLocalRead, err)
   969  	}
   970  
   971  	env := m.Workspace()
   972  
   973  	localState, err := localB.State(env)
   974  	if err != nil {
   975  		return nil, fmt.Errorf(errBackendLocalRead, err)
   976  	}
   977  	if err := localState.RefreshState(); err != nil {
   978  		return nil, fmt.Errorf(errBackendLocalRead, err)
   979  	}
   980  
   981  	// If the local state is not empty, we need to potentially do a
   982  	// state migration to the new backend (with user permission), unless the
   983  	// destination is also "local"
   984  	if localS := localState.State(); !localS.Empty() {
   985  		// Perform the migration
   986  		err = m.backendMigrateState(&backendMigrateOpts{
   987  			OneType: "local",
   988  			TwoType: c.Type,
   989  			One:     localB,
   990  			Two:     b,
   991  		})
   992  		if err != nil {
   993  			return nil, err
   994  		}
   995  
   996  		// we usually remove the local state after migration to prevent
   997  		// confusion, but adding a default local backend block to the config
   998  		// can get us here too. Don't delete our state if the old and new paths
   999  		// are the same.
  1000  		erase := true
  1001  		if newLocalB, ok := b.(*backendlocal.Local); ok {
  1002  			if localB, ok := localB.(*backendlocal.Local); ok {
  1003  				if newLocalB.StatePath == localB.StatePath {
  1004  					erase = false
  1005  				}
  1006  			}
  1007  		}
  1008  
  1009  		if erase {
  1010  			// We always delete the local state, unless that was our new state too.
  1011  			if err := localState.WriteState(nil); err != nil {
  1012  				return nil, fmt.Errorf(errBackendMigrateLocalDelete, err)
  1013  			}
  1014  			if err := localState.PersistState(); err != nil {
  1015  				return nil, fmt.Errorf(errBackendMigrateLocalDelete, err)
  1016  			}
  1017  		}
  1018  	}
  1019  
  1020  	if m.stateLock {
  1021  		lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
  1022  		defer cancel()
  1023  
  1024  		// Lock the state if we can
  1025  		lockInfo := state.NewLockInfo()
  1026  		lockInfo.Operation = "backend from config"
  1027  
  1028  		lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, m.Ui, m.Colorize())
  1029  		if err != nil {
  1030  			return nil, fmt.Errorf("Error locking state: %s", err)
  1031  		}
  1032  		defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
  1033  	}
  1034  
  1035  	// Store the metadata in our saved state location
  1036  	s := sMgr.State()
  1037  	if s == nil {
  1038  		s = terraform.NewState()
  1039  	}
  1040  	s.Backend = &terraform.BackendState{
  1041  		Type:   c.Type,
  1042  		Config: c.RawConfig.Raw,
  1043  		Hash:   c.Hash,
  1044  	}
  1045  
  1046  	if err := sMgr.WriteState(s); err != nil {
  1047  		return nil, fmt.Errorf(errBackendWriteSaved, err)
  1048  	}
  1049  	if err := sMgr.PersistState(); err != nil {
  1050  		return nil, fmt.Errorf(errBackendWriteSaved, err)
  1051  	}
  1052  
  1053  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1054  		"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
  1055  
  1056  	// Return the backend
  1057  	return b, nil
  1058  }
  1059  
  1060  // Changing a previously saved backend.
  1061  func (m *Meta) backend_C_r_S_changed(
  1062  	c *config.Backend, sMgr state.State, output bool) (backend.Backend, error) {
  1063  	if output {
  1064  		// Notify the user
  1065  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1066  			"[reset]%s\n\n",
  1067  			strings.TrimSpace(outputBackendReconfigure))))
  1068  	}
  1069  
  1070  	// Get the old state
  1071  	s := sMgr.State()
  1072  
  1073  	// Get the backend
  1074  	b, err := m.backendInitFromConfig(c)
  1075  	if err != nil {
  1076  		return nil, fmt.Errorf(
  1077  			"Error initializing new backend: %s", err)
  1078  	}
  1079  
  1080  	// Check with the user if we want to migrate state
  1081  	copy := m.forceInitCopy
  1082  	if !copy {
  1083  		copy, err = m.confirm(&terraform.InputOpts{
  1084  			Id:          "backend-migrate-to-new",
  1085  			Query:       fmt.Sprintf("Do you want to copy the state from %q?", s.Backend.Type),
  1086  			Description: strings.TrimSpace(fmt.Sprintf(inputBackendMigrateChange, s.Backend.Type, c.Type)),
  1087  		})
  1088  		if err != nil {
  1089  			return nil, fmt.Errorf(
  1090  				"Error asking for state copy action: %s", err)
  1091  		}
  1092  	}
  1093  
  1094  	// If we are, then we need to initialize the old backend and
  1095  	// perform the copy.
  1096  	if copy {
  1097  		// Grab the existing backend
  1098  		oldB, err := m.backend_C_r_S_unchanged(c, sMgr)
  1099  		if err != nil {
  1100  			return nil, fmt.Errorf(
  1101  				"Error loading previously configured backend: %s", err)
  1102  		}
  1103  
  1104  		// Perform the migration
  1105  		err = m.backendMigrateState(&backendMigrateOpts{
  1106  			OneType: s.Backend.Type,
  1107  			TwoType: c.Type,
  1108  			One:     oldB,
  1109  			Two:     b,
  1110  		})
  1111  		if err != nil {
  1112  			return nil, err
  1113  		}
  1114  	}
  1115  
  1116  	if m.stateLock {
  1117  		lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
  1118  		defer cancel()
  1119  
  1120  		// Lock the state if we can
  1121  		lockInfo := state.NewLockInfo()
  1122  		lockInfo.Operation = "backend from config"
  1123  
  1124  		lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, m.Ui, m.Colorize())
  1125  		if err != nil {
  1126  			return nil, fmt.Errorf("Error locking state: %s", err)
  1127  		}
  1128  		defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
  1129  	}
  1130  
  1131  	// Update the backend state
  1132  	s = sMgr.State()
  1133  	if s == nil {
  1134  		s = terraform.NewState()
  1135  	}
  1136  	s.Backend = &terraform.BackendState{
  1137  		Type:   c.Type,
  1138  		Config: c.RawConfig.Raw,
  1139  		Hash:   c.Hash,
  1140  	}
  1141  
  1142  	if err := sMgr.WriteState(s); err != nil {
  1143  		return nil, fmt.Errorf(errBackendWriteSaved, err)
  1144  	}
  1145  	if err := sMgr.PersistState(); err != nil {
  1146  		return nil, fmt.Errorf(errBackendWriteSaved, err)
  1147  	}
  1148  
  1149  	if output {
  1150  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1151  			"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
  1152  	}
  1153  
  1154  	return b, nil
  1155  }
  1156  
  1157  // Initiailizing an unchanged saved backend
  1158  func (m *Meta) backend_C_r_S_unchanged(
  1159  	c *config.Backend, sMgr state.State) (backend.Backend, error) {
  1160  	s := sMgr.State()
  1161  
  1162  	// it's possible for a backend to be unchanged, and the config itself to
  1163  	// have changed by moving a parameter from the config to `-backend-config`
  1164  	// In this case we only need to update the Hash.
  1165  	if c != nil && s.Backend.Hash != c.Hash {
  1166  		s.Backend.Hash = c.Hash
  1167  		if err := sMgr.WriteState(s); err != nil {
  1168  			return nil, fmt.Errorf(errBackendWriteSaved, err)
  1169  		}
  1170  	}
  1171  
  1172  	// Create the config. We do this from the backend state since this
  1173  	// has the complete configuration data whereas the config itself
  1174  	// may require input.
  1175  	rawC, err := config.NewRawConfig(s.Backend.Config)
  1176  	if err != nil {
  1177  		return nil, fmt.Errorf("Error configuring backend: %s", err)
  1178  	}
  1179  	config := terraform.NewResourceConfig(rawC)
  1180  
  1181  	// Get the backend
  1182  	f := backendinit.Backend(s.Backend.Type)
  1183  	if f == nil {
  1184  		return nil, fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type)
  1185  	}
  1186  	b := f()
  1187  
  1188  	// Configure
  1189  	if err := b.Configure(config); err != nil {
  1190  		return nil, fmt.Errorf(errBackendSavedConfig, s.Backend.Type, err)
  1191  	}
  1192  
  1193  	return b, nil
  1194  }
  1195  
  1196  // Initiailizing a changed saved backend with legacy remote state.
  1197  func (m *Meta) backend_C_R_S_changed(
  1198  	c *config.Backend, sMgr state.State) (backend.Backend, error) {
  1199  	// Notify the user
  1200  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1201  		"[reset]%s\n\n",
  1202  		strings.TrimSpace(outputBackendSavedWithLegacyChanged))))
  1203  
  1204  	// Reconfigure the backend first
  1205  	if _, err := m.backend_C_r_S_changed(c, sMgr, false); err != nil {
  1206  		return nil, err
  1207  	}
  1208  
  1209  	// Handle the case where we have all set but unchanged
  1210  	b, err := m.backend_C_R_S_unchanged(c, sMgr, false)
  1211  	if err != nil {
  1212  		return nil, err
  1213  	}
  1214  
  1215  	// Output success message
  1216  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1217  		"[reset][green]\n\n"+
  1218  			strings.TrimSpace(successBackendReconfigureWithLegacy), c.Type)))
  1219  
  1220  	return b, nil
  1221  }
  1222  
  1223  // Initiailizing an unchanged saved backend with legacy remote state.
  1224  func (m *Meta) backend_C_R_S_unchanged(
  1225  	c *config.Backend, sMgr state.State, output bool) (backend.Backend, error) {
  1226  	if output {
  1227  		// Notify the user
  1228  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1229  			"[reset]%s\n\n",
  1230  			strings.TrimSpace(outputBackendSavedWithLegacy))))
  1231  	}
  1232  
  1233  	// Load the backend from the state
  1234  	s := sMgr.State()
  1235  	b, err := m.backendInitFromSaved(s.Backend)
  1236  	if err != nil {
  1237  		return nil, err
  1238  	}
  1239  
  1240  	// Ask if the user wants to move their legacy remote state
  1241  	copy := m.forceInitCopy
  1242  	if !copy {
  1243  		copy, err = m.confirm(&terraform.InputOpts{
  1244  			Id: "backend-migrate-to-new",
  1245  			Query: fmt.Sprintf(
  1246  				"Do you want to copy the legacy remote state from %q?",
  1247  				s.Remote.Type),
  1248  			Description: strings.TrimSpace(inputBackendMigrateLegacy),
  1249  		})
  1250  		if err != nil {
  1251  			return nil, fmt.Errorf(
  1252  				"Error asking for state copy action: %s", err)
  1253  		}
  1254  	}
  1255  
  1256  	// If the user wants a copy, copy!
  1257  	if copy {
  1258  		// Initialize the legacy backend
  1259  		oldB, err := m.backendInitFromLegacy(s.Remote)
  1260  		if err != nil {
  1261  			return nil, err
  1262  		}
  1263  
  1264  		// Perform the migration
  1265  		err = m.backendMigrateState(&backendMigrateOpts{
  1266  			OneType: s.Remote.Type,
  1267  			TwoType: s.Backend.Type,
  1268  			One:     oldB,
  1269  			Two:     b,
  1270  		})
  1271  		if err != nil {
  1272  			return nil, err
  1273  		}
  1274  	}
  1275  
  1276  	if m.stateLock {
  1277  		lockCtx, cancel := context.WithTimeout(context.Background(), m.stateLockTimeout)
  1278  		defer cancel()
  1279  
  1280  		// Lock the state if we can
  1281  		lockInfo := state.NewLockInfo()
  1282  		lockInfo.Operation = "backend from config"
  1283  
  1284  		lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, m.Ui, m.Colorize())
  1285  		if err != nil {
  1286  			return nil, fmt.Errorf("Error locking state: %s", err)
  1287  		}
  1288  		defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize())
  1289  	}
  1290  
  1291  	// Unset the remote state
  1292  	s = sMgr.State()
  1293  	if s == nil {
  1294  		s = terraform.NewState()
  1295  	}
  1296  	s.Remote = nil
  1297  
  1298  	if err := sMgr.WriteState(s); err != nil {
  1299  		return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err)
  1300  	}
  1301  	if err := sMgr.PersistState(); err != nil {
  1302  		return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err)
  1303  	}
  1304  
  1305  	if output {
  1306  		m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1307  			"[reset][green]\n\n"+
  1308  				strings.TrimSpace(successBackendLegacyUnset), s.Backend.Type)))
  1309  	}
  1310  
  1311  	return b, nil
  1312  }
  1313  
  1314  //-------------------------------------------------------------------
  1315  // Reusable helper functions for backend management
  1316  //-------------------------------------------------------------------
  1317  
  1318  func (m *Meta) backendInitFromConfig(c *config.Backend) (backend.Backend, error) {
  1319  	// Create the config.
  1320  	config := terraform.NewResourceConfig(c.RawConfig)
  1321  
  1322  	// Get the backend
  1323  	f := backendinit.Backend(c.Type)
  1324  	if f == nil {
  1325  		return nil, fmt.Errorf(strings.TrimSpace(errBackendNewUnknown), c.Type)
  1326  	}
  1327  	b := f()
  1328  
  1329  	// TODO: test
  1330  	// Ask for input if we have input enabled
  1331  	if m.Input() {
  1332  		var err error
  1333  		config, err = b.Input(m.UIInput(), config)
  1334  		if err != nil {
  1335  			return nil, fmt.Errorf(
  1336  				"Error asking for input to configure the backend %q: %s",
  1337  				c.Type, err)
  1338  		}
  1339  	}
  1340  
  1341  	// Validate
  1342  	warns, errs := b.Validate(config)
  1343  	for _, warning := range warns {
  1344  		// We just write warnings directly to the UI. This isn't great
  1345  		// since we're a bit deep here to be pushing stuff out into the
  1346  		// UI, but sufficient to let us print out deprecation warnings
  1347  		// and the like.
  1348  		m.Ui.Warn(warning)
  1349  	}
  1350  	if len(errs) > 0 {
  1351  		return nil, fmt.Errorf(
  1352  			"Error configuring the backend %q: %s",
  1353  			c.Type, multierror.Append(nil, errs...))
  1354  	}
  1355  
  1356  	// Configure
  1357  	if err := b.Configure(config); err != nil {
  1358  		return nil, fmt.Errorf(errBackendNewConfig, c.Type, err)
  1359  	}
  1360  
  1361  	return b, nil
  1362  }
  1363  
  1364  func (m *Meta) backendInitFromLegacy(s *terraform.RemoteState) (backend.Backend, error) {
  1365  	// We need to convert the config to map[string]interface{} since that
  1366  	// is what the backends expect.
  1367  	var configMap map[string]interface{}
  1368  	if err := mapstructure.Decode(s.Config, &configMap); err != nil {
  1369  		return nil, fmt.Errorf("Error configuring remote state: %s", err)
  1370  	}
  1371  
  1372  	// Create the config
  1373  	rawC, err := config.NewRawConfig(configMap)
  1374  	if err != nil {
  1375  		return nil, fmt.Errorf("Error configuring remote state: %s", err)
  1376  	}
  1377  	config := terraform.NewResourceConfig(rawC)
  1378  
  1379  	// Get the backend
  1380  	f := backendinit.Backend(s.Type)
  1381  	if f == nil {
  1382  		return nil, fmt.Errorf(strings.TrimSpace(errBackendLegacyUnknown), s.Type)
  1383  	}
  1384  	b := f()
  1385  
  1386  	// Configure
  1387  	if err := b.Configure(config); err != nil {
  1388  		return nil, fmt.Errorf(errBackendLegacyConfig, err)
  1389  	}
  1390  
  1391  	return b, nil
  1392  }
  1393  
  1394  func (m *Meta) backendInitFromSaved(s *terraform.BackendState) (backend.Backend, error) {
  1395  	// Create the config. We do this from the backend state since this
  1396  	// has the complete configuration data whereas the config itself
  1397  	// may require input.
  1398  	rawC, err := config.NewRawConfig(s.Config)
  1399  	if err != nil {
  1400  		return nil, fmt.Errorf("Error configuring backend: %s", err)
  1401  	}
  1402  	config := terraform.NewResourceConfig(rawC)
  1403  
  1404  	// Get the backend
  1405  	f := backendinit.Backend(s.Type)
  1406  	if f == nil {
  1407  		return nil, fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Type)
  1408  	}
  1409  	b := f()
  1410  
  1411  	// Configure
  1412  	if err := b.Configure(config); err != nil {
  1413  		return nil, fmt.Errorf(errBackendSavedConfig, s.Type, err)
  1414  	}
  1415  
  1416  	return b, nil
  1417  }
  1418  
  1419  func (m *Meta) backendInitRequired(reason string) {
  1420  	m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
  1421  		"[reset]"+strings.TrimSpace(errBackendInit)+"\n", reason)))
  1422  }
  1423  
  1424  //-------------------------------------------------------------------
  1425  // Output constants and initialization code
  1426  //-------------------------------------------------------------------
  1427  
  1428  // errBackendInitRequired is the final error message shown when reinit
  1429  // is required for some reason. The error message includes the reason.
  1430  var errBackendInitRequired = errors.New(
  1431  	"Initialization required. Please see the error message above.")
  1432  
  1433  const errBackendLegacyConfig = `
  1434  One or more errors occurred while configuring the legacy remote state.
  1435  If fixing these errors requires changing your remote state configuration,
  1436  you must switch your configuration to the new remote backend configuration.
  1437  You can learn more about remote backends at the URL below:
  1438  
  1439  https://www.terraform.io/docs/backends/index.html
  1440  
  1441  The error(s) configuring the legacy remote state:
  1442  
  1443  %s
  1444  `
  1445  
  1446  const errBackendLegacyUnknown = `
  1447  The legacy remote state type %q could not be found.
  1448  
  1449  Terraform 0.9.0 shipped with backwards compatibility for all built-in
  1450  legacy remote state types. This error may mean that you were using a
  1451  custom Terraform build that perhaps supported a different type of
  1452  remote state.
  1453  
  1454  Please check with the creator of the remote state above and try again.
  1455  `
  1456  
  1457  const errBackendLocalRead = `
  1458  Error reading local state: %s
  1459  
  1460  Terraform is trying to read your local state to determine if there is
  1461  state to migrate to your newly configured backend. Terraform can't continue
  1462  without this check because that would risk losing state. Please resolve the
  1463  error above and try again.
  1464  `
  1465  
  1466  const errBackendMigrateLocalDelete = `
  1467  Error deleting local state after migration: %s
  1468  
  1469  Your local state is deleted after successfully migrating it to the newly
  1470  configured backend. As part of the deletion process, a backup is made at
  1471  the standard backup path unless explicitly asked not to. To cleanly operate
  1472  with a backend, we must delete the local state file. Please resolve the
  1473  issue above and retry the command.
  1474  `
  1475  
  1476  const errBackendMigrateNew = `
  1477  Error migrating local state to backend: %s
  1478  
  1479  Your local state remains intact and unmodified. Please resolve the error
  1480  above and try again.
  1481  `
  1482  
  1483  const errBackendNewConfig = `
  1484  Error configuring the backend %q: %s
  1485  
  1486  Please update the configuration in your Terraform files to fix this error
  1487  then run this command again.
  1488  `
  1489  
  1490  const errBackendNewRead = `
  1491  Error reading newly configured backend state: %s
  1492  
  1493  Terraform is trying to read the state from your newly configured backend
  1494  to determine the copy process for your existing state. Backends are expected
  1495  to not error even if there is no state yet written. Please resolve the
  1496  error above and try again.
  1497  `
  1498  
  1499  const errBackendNewUnknown = `
  1500  The backend %q could not be found.
  1501  
  1502  This is the backend specified in your Terraform configuration file.
  1503  This error could be a simple typo in your configuration, but it can also
  1504  be caused by using a Terraform version that doesn't support the specified
  1505  backend type. Please check your configuration and your Terraform version.
  1506  
  1507  If you'd like to run Terraform and store state locally, you can fix this
  1508  error by removing the backend configuration from your configuration.
  1509  `
  1510  
  1511  const errBackendRemoteRead = `
  1512  Error reading backend state: %s
  1513  
  1514  Terraform is trying to read the state from your configured backend to
  1515  determine if there is any migration steps necessary. Terraform can't continue
  1516  without this check because that would risk losing state. Please resolve the
  1517  error above and try again.
  1518  `
  1519  
  1520  const errBackendSavedConfig = `
  1521  Error configuring the backend %q: %s
  1522  
  1523  Please update the configuration in your Terraform files to fix this error.
  1524  If you'd like to update the configuration interactively without storing
  1525  the values in your configuration, run "terraform init".
  1526  `
  1527  
  1528  const errBackendSavedUnsetConfig = `
  1529  Error configuring the existing backend %q: %s
  1530  
  1531  Terraform must configure the existing backend in order to copy the state
  1532  from the existing backend, as requested. Please resolve the error and try
  1533  again. If you choose to not copy the existing state, Terraform will not
  1534  configure the backend. If the configuration is invalid, please update your
  1535  Terraform configuration with proper configuration for this backend first
  1536  before unsetting the backend.
  1537  `
  1538  
  1539  const errBackendSavedUnknown = `
  1540  The backend %q could not be found.
  1541  
  1542  This is the backend that this Terraform environment is configured to use
  1543  both in your configuration and saved locally as your last-used backend.
  1544  If it isn't found, it could mean an alternate version of Terraform was
  1545  used with this configuration. Please use the proper version of Terraform that
  1546  contains support for this backend.
  1547  
  1548  If you'd like to force remove this backend, you must update your configuration
  1549  to not use the backend and run "terraform init" (or any other command) again.
  1550  `
  1551  
  1552  const errBackendClearLegacy = `
  1553  Error clearing the legacy remote state configuration: %s
  1554  
  1555  Terraform completed configuring your backend. It is now safe to remove
  1556  the legacy remote state configuration, but an error occurred while trying
  1557  to do so. Please look at the error above, resolve it, and try again.
  1558  `
  1559  
  1560  const errBackendClearSaved = `
  1561  Error clearing the backend configuration: %s
  1562  
  1563  Terraform removes the saved backend configuration when you're removing a
  1564  configured backend. This must be done so future Terraform runs know to not
  1565  use the backend configuration. Please look at the error above, resolve it,
  1566  and try again.
  1567  `
  1568  
  1569  const errBackendInit = `
  1570  [reset][bold][yellow]Backend reinitialization required. Please run "terraform init".[reset]
  1571  [yellow]Reason: %s
  1572  
  1573  The "backend" is the interface that Terraform uses to store state,
  1574  perform operations, etc. If this message is showing up, it means that the
  1575  Terraform configuration you're using is using a custom configuration for
  1576  the Terraform backend.
  1577  
  1578  Changes to backend configurations require reinitialization. This allows
  1579  Terraform to setup the new configuration, copy existing state, etc. This is
  1580  only done during "terraform init". Please run that command now then try again.
  1581  
  1582  If the change reason above is incorrect, please verify your configuration
  1583  hasn't changed and try again. At this point, no changes to your existing
  1584  configuration or state have been made.
  1585  `
  1586  
  1587  const errBackendWriteSaved = `
  1588  Error saving the backend configuration: %s
  1589  
  1590  Terraform saves the complete backend configuration in a local file for
  1591  configuring the backend on future operations. This cannot be disabled. Errors
  1592  are usually due to simple file permission errors. Please look at the error
  1593  above, resolve it, and try again.
  1594  `
  1595  
  1596  const errBackendPlanBoth = `
  1597  The plan file contained both a legacy remote state and backend configuration.
  1598  This is not allowed. Please recreate the plan file with the latest version of
  1599  Terraform.
  1600  `
  1601  
  1602  const errBackendPlanLineageDiff = `
  1603  The plan file contains a state with a differing lineage than the current
  1604  state. By continuing, your current state would be overwritten by the state
  1605  in the plan. Please either update the plan with the latest state or delete
  1606  your current state and try again.
  1607  
  1608  "Lineage" is a unique identifier generated only once on the creation of
  1609  a new, empty state. If these values differ, it means they were created new
  1610  at different times. Therefore, Terraform must assume that they're completely
  1611  different states.
  1612  
  1613  The most common cause of seeing this error is using a plan that was
  1614  created against a different state. Perhaps the plan is very old and the
  1615  state has since been recreated, or perhaps the plan was against a competely
  1616  different infrastructure.
  1617  `
  1618  
  1619  const errBackendPlanStateFlag = `
  1620  The -state and -state-out flags cannot be set with a plan that has a remote
  1621  state. The plan itself contains the configuration for the remote backend to
  1622  store state. The state will be written there for consistency.
  1623  
  1624  If you wish to change this behavior, please create a plan from local state.
  1625  You may use the state flags with plans from local state to affect where
  1626  the final state is written.
  1627  `
  1628  
  1629  const errBackendPlanOlder = `
  1630  This plan was created against an older state than is current. Please create
  1631  a new plan file against the latest state and try again.
  1632  
  1633  Terraform doesn't allow you to run plans that were created from older
  1634  states since it doesn't properly represent the latest changes Terraform
  1635  may have made, and can result in unsafe behavior.
  1636  
  1637  Plan Serial:    %[1]d
  1638  Current Serial: %[2]d
  1639  `
  1640  
  1641  const inputBackendMigrateChange = `
  1642  Would you like to copy the state from your prior backend %q to the
  1643  newly configured %q backend? If you're reconfiguring the same backend,
  1644  answering "yes" or "no" shouldn't make a difference. Please answer exactly
  1645  "yes" or "no".
  1646  `
  1647  
  1648  const inputBackendMigrateLegacy = `
  1649  Terraform can copy the existing state in your legacy remote state
  1650  backend to your newly configured backend. Please answer "yes" or "no".
  1651  `
  1652  
  1653  const inputBackendMigrateLegacyLocal = `
  1654  Terraform can copy the existing state in your legacy remote state
  1655  backend to your local state. Please answer "yes" or "no".
  1656  `
  1657  
  1658  const inputBackendMigrateLocal = `
  1659  Terraform has detected you're unconfiguring your previously set backend.
  1660  Would you like to copy the state from %q to local state? Please answer
  1661  "yes" or "no". If you answer "no", you will start with a blank local state.
  1662  `
  1663  
  1664  const outputBackendConfigureWithLegacy = `
  1665  [reset][bold]New backend configuration detected with legacy remote state![reset]
  1666  
  1667  Terraform has detected that you're attempting to configure a new backend.
  1668  At the same time, legacy remote state configuration was found. Terraform will
  1669  first configure the new backend, and then ask if you'd like to migrate
  1670  your remote state to the new backend.
  1671  `
  1672  
  1673  const outputBackendReconfigure = `
  1674  [reset][bold]Backend configuration changed![reset]
  1675  
  1676  Terraform has detected that the configuration specified for the backend
  1677  has changed. Terraform will now reconfigure for this backend. If you didn't
  1678  intend to reconfigure your backend please undo any changes to the "backend"
  1679  section in your Terraform configuration.
  1680  `
  1681  
  1682  const outputBackendSavedWithLegacy = `
  1683  [reset][bold]Legacy remote state was detected![reset]
  1684  
  1685  Terraform has detected you still have legacy remote state enabled while
  1686  also having a backend configured. Terraform will now ask if you want to
  1687  migrate your legacy remote state data to the configured backend.
  1688  `
  1689  
  1690  const outputBackendSavedWithLegacyChanged = `
  1691  [reset][bold]Legacy remote state was detected while also changing your current backend!reset]
  1692  
  1693  Terraform has detected that you have legacy remote state, a configured
  1694  current backend, and you're attempting to reconfigure your backend. To handle
  1695  all of these changes, Terraform will first reconfigure your backend. After
  1696  this, Terraform will handle optionally copying your legacy remote state
  1697  into the newly configured backend.
  1698  `
  1699  
  1700  const outputBackendUnsetWithLegacy = `
  1701  [reset][bold]Detected a request to unset the backend with legacy remote state present![reset]
  1702  
  1703  Terraform has detected that you're attempting to unset a previously configured
  1704  backend (by not having the "backend" configuration set in your Terraform files).
  1705  At the same time, legacy remote state was detected. To handle this complex
  1706  scenario, Terraform will first unset your configured backend, and then
  1707  ask you how to handle the legacy remote state. This will be multi-step
  1708  process.
  1709  `
  1710  
  1711  const successBackendLegacyUnset = `
  1712  Terraform has successfully migrated from legacy remote state to your
  1713  configured backend (%q).
  1714  `
  1715  
  1716  const successBackendReconfigureWithLegacy = `
  1717  Terraform has successfully reconfigured your backend and migrate
  1718  from legacy remote state to the new backend.
  1719  `
  1720  
  1721  const successBackendUnset = `
  1722  Successfully unset the backend %q. Terraform will now operate locally.
  1723  `
  1724  
  1725  const successBackendSet = `
  1726  Successfully configured the backend %q! Terraform will automatically
  1727  use this backend unless the backend configuration changes.
  1728  `
  1729  
  1730  const warnBackendLegacy = `
  1731  Deprecation warning: This environment is configured to use legacy remote state.
  1732  Remote state changed significantly in Terraform 0.9. Please update your remote
  1733  state configuration to use the new 'backend' settings. For now, Terraform
  1734  will continue to use your existing settings. Legacy remote state support
  1735  will be removed in Terraform 0.11.
  1736  
  1737  You can find a guide for upgrading here:
  1738  
  1739  https://www.terraform.io/docs/backends/legacy-0-8.html
  1740  `