Skip to content

Commit d72a413

Browse files
authored
command/meta_backend: Prompt to select workspace before saving backend config (#29945)
When terraform detects that a user has no workspaces that map to their current configuration, it will prompt the user to create a new workspace and enter a value name. If the user ignores the prompt and exits it, the legacy backend (terraform.tfstate) will be left in a awkward state: 1. This saved backend config will show a diff for the JSON attributes "serial", "tags" and "hash" 2. "Terraform workspace list" will show an empty list 3. "Terraform apply" will run successfully using the previous workspace, from the previous config, not the one from the current saved backend config 4. The cloud config is not reflective of the current working directory Solution: If the user exits the prompt, the saved backend config should not be updated because they did not select a new workspace. They are back at the beginning where they are force to re run the init cmd again before proceeding with new changes.
1 parent d8a1279 commit d72a413

File tree

1 file changed

+41
-17
lines changed

1 file changed

+41
-17
lines changed

internal/command/meta_backend.go

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,6 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics
9797
b, backendDiags = m.backendFromConfig(opts)
9898
diags = diags.Append(backendDiags)
9999

100-
if opts.Init && b != nil && !diags.HasErrors() {
101-
// Its possible that the currently selected workspace doesn't exist, so
102-
// we call selectWorkspace to ensure an existing workspace is selected.
103-
if err := m.selectWorkspace(b); err != nil {
104-
diags = diags.Append(err)
105-
}
106-
}
107-
108100
if diags.HasErrors() {
109101
return nil, diags
110102
}
@@ -232,8 +224,11 @@ func (m *Meta) selectWorkspace(b backend.Backend) error {
232224
Query: "\n[reset][bold][yellow]No workspaces found.[reset]",
233225
Description: fmt.Sprintf(inputCloudInitCreateWorkspace, strings.Join(c.WorkspaceMapping.Tags, ", ")),
234226
})
227+
if err != nil {
228+
return fmt.Errorf("Couldn't create initial workspace: %w", err)
229+
}
235230
name = strings.TrimSpace(name)
236-
if err != nil || name == "" {
231+
if name == "" {
237232
return fmt.Errorf("Couldn't create initial workspace: no name provided")
238233
}
239234
log.Printf("[TRACE] Meta.selectWorkspace: selecting the new TFC workspace requested by the user (%s)", name)
@@ -610,7 +605,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
610605

611606
return m.backend_c_r_S(c, cHash, sMgr, true)
612607

613-
// Configuring a backend for the first time.
608+
// Configuring a backend for the first time or -reconfigure flag was used
614609
case c != nil && s.Backend.Empty():
615610
log.Printf("[TRACE] Meta.Backend: moving from default local state only to %q backend", c.Type)
616611
if !opts.Init {
@@ -631,9 +626,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
631626
}
632627
return nil, diags
633628
}
634-
635-
return m.backend_C_r_s(c, cHash, sMgr)
636-
629+
return m.backend_C_r_s(c, cHash, sMgr, opts)
637630
// Potentially changing a backend configuration
638631
case c != nil && !s.Backend.Empty():
639632
// We are not going to migrate if...
@@ -643,7 +636,15 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
643636
// AND we're not providing any overrides. An override can mean a change overriding an unchanged backend block (indicated by the hash value).
644637
if (uint64(cHash) == s.Backend.Hash) && (!opts.Init || opts.ConfigOverride == nil) {
645638
log.Printf("[TRACE] Meta.Backend: using already-initialized, unchanged %q backend configuration", c.Type)
646-
return m.savedBackend(sMgr)
639+
savedBackend, diags := m.savedBackend(sMgr)
640+
// Verify that selected workspace exist. Otherwise prompt user to create one
641+
if opts.Init && savedBackend != nil {
642+
if err := m.selectWorkspace(savedBackend); err != nil {
643+
diags = diags.Append(err)
644+
return nil, diags
645+
}
646+
}
647+
return savedBackend, diags
647648
}
648649

649650
// If our configuration (the result of both the literal configuration and given
@@ -665,6 +666,13 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
665666
if moreDiags.HasErrors() {
666667
return nil, diags
667668
}
669+
// Verify that selected workspace exist. Otherwise prompt user to create one
670+
if opts.Init && savedBackend != nil {
671+
if err := m.selectWorkspace(savedBackend); err != nil {
672+
diags = diags.Append(err)
673+
return nil, diags
674+
}
675+
}
668676

669677
return savedBackend, diags
670678
}
@@ -685,7 +693,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
685693
}
686694

687695
log.Printf("[WARN] backend config has changed since last init")
688-
return m.backend_C_r_S_changed(c, cHash, sMgr, true)
696+
return m.backend_C_r_S_changed(c, cHash, sMgr, true, opts)
689697

690698
default:
691699
diags = diags.Append(fmt.Errorf(
@@ -897,7 +905,7 @@ func (m *Meta) backend_c_r_S(c *configs.Backend, cHash int, sMgr *clistate.Local
897905
}
898906

899907
// Configuring a backend for the first time.
900-
func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
908+
func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState, opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
901909
var diags tfdiags.Diagnostics
902910

903911
// Grab a purely local backend to get the local state if it exists
@@ -1017,6 +1025,14 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
10171025
Hash: uint64(cHash),
10181026
}
10191027

1028+
// Verify that selected workspace exist. Otherwise prompt user to create one
1029+
if opts.Init && b != nil {
1030+
if err := m.selectWorkspace(b); err != nil {
1031+
diags = diags.Append(err)
1032+
return nil, diags
1033+
}
1034+
}
1035+
10201036
if err := sMgr.WriteState(s); err != nil {
10211037
diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
10221038
return nil, diags
@@ -1037,7 +1053,7 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
10371053
}
10381054

10391055
// Changing a previously saved backend.
1040-
func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool) (backend.Backend, tfdiags.Diagnostics) {
1056+
func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool, opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
10411057
var diags tfdiags.Diagnostics
10421058

10431059
// Get the old state
@@ -1133,6 +1149,14 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista
11331149
Hash: uint64(cHash),
11341150
}
11351151

1152+
// Verify that selected workspace exist. Otherwise prompt user to create one
1153+
if opts.Init && b != nil {
1154+
if err := m.selectWorkspace(b); err != nil {
1155+
diags = diags.Append(err)
1156+
return b, diags
1157+
}
1158+
}
1159+
11361160
if err := sMgr.WriteState(s); err != nil {
11371161
diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
11381162
return nil, diags

0 commit comments

Comments
 (0)