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