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