Skip to content

Commit 87b3390

Browse files
authored
fix: Make init backend messages JSON-compatible (#37920)
* fix: Make all init backend messages JSON-compatible Fixes #37911 by converting all backend configuration messages to use the View abstraction, which properly formats output as JSON when the -json flag is used. Previously, certain messages were output directly using the legacy Ui abstraction, bypassing JSON formatting. Changes: - Added 11 new message codes to init.go MessageRegistry with both human and JSON formatted values - Replaced 11 direct m.Ui.Output() calls in meta_backend.go with View abstraction calls - Fixed output in backend_C_r_s(), backend_C_r_S_changed(), backend_c_r_S(), and stateStore_c_S() functions All init tests pass successfully. * docs: Add changelog entry for init JSON backend messages fix * cleanup: Remove unused backend output constants The following constants were moved to init.go as message registry entries and are no longer used in meta_backend.go: - outputBackendMigrateChange - outputBackendMigrateLocal - outputStateStoreMigrateLocal - outputBackendReconfigure - successBackendUnset - successBackendSet
1 parent 65a23ed commit 87b3390

File tree

3 files changed

+118
-48
lines changed

3 files changed

+118
-48
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: BUG FIXES
2+
body: 'cli: Fixed `terraform init -json` to properly format all backend configuration messages as JSON instead of plain text'
3+
time: 2025-11-19T10:30:00.000000Z
4+
custom:
5+
Issue: "37911"

internal/command/meta_backend.go

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,10 +1132,11 @@ func (m *Meta) backend_c_r_S(
11321132
// Get the backend type for output
11331133
backendType := s.Backend.Type
11341134

1135+
view := views.NewInit(vt, m.View)
11351136
if cloudMode == cloud.ConfigMigrationOut {
1136-
m.Ui.Output("Migrating from HCP Terraform or Terraform Enterprise to local state.")
1137+
view.Output(views.BackendCloudMigrateLocalMessage)
11371138
} else {
1138-
m.Ui.Output(fmt.Sprintf(strings.TrimSpace(outputBackendMigrateLocal), s.Backend.Type))
1139+
view.Output(views.BackendMigrateLocalMessage, s.Backend.Type)
11391140
}
11401141

11411142
// Grab a purely local backend to get the local state if it exists
@@ -1177,9 +1178,7 @@ func (m *Meta) backend_c_r_S(
11771178
}
11781179

11791180
if output {
1180-
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
1181-
"[reset][green]\n\n"+
1182-
strings.TrimSpace(successBackendUnset), backendType)))
1181+
view.Output(views.BackendConfiguredUnsetMessage, backendType)
11831182
}
11841183

11851184
// Return no backend
@@ -1348,8 +1347,8 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
13481347
// By now the backend is successfully configured. If using HCP Terraform, the success
13491348
// message is handled as part of the final init message
13501349
if _, ok := b.(*cloud.Cloud); !ok {
1351-
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
1352-
"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
1350+
view := views.NewInit(vt, m.View)
1351+
view.Output(views.BackendConfiguredSuccessMessage, s.Backend.Type)
13531352
}
13541353

13551354
return b, diags
@@ -1377,23 +1376,19 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista
13771376

13781377
if output {
13791378
// Notify the user
1379+
view := views.NewInit(vt, m.View)
13801380
switch cloudMode {
13811381
case cloud.ConfigChangeInPlace:
1382-
m.Ui.Output("HCP Terraform configuration has changed.")
1382+
view.Output(views.BackendCloudChangeInPlaceMessage)
13831383
case cloud.ConfigMigrationIn:
1384-
m.Ui.Output(fmt.Sprintf("Migrating from backend %q to HCP Terraform.", s.Backend.Type))
1384+
view.Output(views.BackendMigrateToCloudMessage, s.Backend.Type)
13851385
case cloud.ConfigMigrationOut:
1386-
m.Ui.Output(fmt.Sprintf("Migrating from HCP Terraform to backend %q.", c.Type))
1386+
view.Output(views.BackendMigrateFromCloudMessage, c.Type)
13871387
default:
13881388
if s.Backend.Type != c.Type {
1389-
output := fmt.Sprintf(outputBackendMigrateChange, s.Backend.Type, c.Type)
1390-
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
1391-
"[reset]%s\n",
1392-
strings.TrimSpace(output))))
1389+
view.Output(views.BackendMigrateTypeChangeMessage, s.Backend.Type, c.Type)
13931390
} else {
1394-
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
1395-
"[reset]%s\n",
1396-
strings.TrimSpace(outputBackendReconfigure))))
1391+
view.Output(views.BackendReconfigureMessage)
13971392
}
13981393
}
13991394
}
@@ -1479,8 +1474,8 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista
14791474
// By now the backend is successfully configured. If using HCP Terraform, the success
14801475
// message is handled as part of the final init message
14811476
if _, ok := b.(*cloud.Cloud); !ok {
1482-
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
1483-
"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
1477+
view := views.NewInit(vt, m.View)
1478+
view.Output(views.BackendConfiguredSuccessMessage, s.Backend.Type)
14841479
}
14851480
}
14861481

@@ -1866,7 +1861,8 @@ func (m *Meta) stateStore_c_S(ssSMgr *clistate.LocalState, viewType arguments.Vi
18661861
s := ssSMgr.State()
18671862
stateStoreType := s.StateStore.Type
18681863

1869-
m.Ui.Output(fmt.Sprintf(strings.TrimSpace(outputStateStoreMigrateLocal), stateStoreType))
1864+
view := views.NewInit(viewType, m.View)
1865+
view.Output(views.StateMigrateLocalMessage, stateStoreType)
18701866

18711867
// Grab a purely local backend to get the local state if it exists
18721868
localB, moreDiags := m.Backend(&BackendOpts{ForceLocal: true, Init: true})
@@ -2625,25 +2621,6 @@ func (m *Meta) StateStoreProviderFactoryFromConfigState(cfgState *workdir.StateS
26252621
// Output constants and initialization code
26262622
//-------------------------------------------------------------------
26272623

2628-
const outputBackendMigrateChange = `
2629-
Terraform detected that the backend type changed from %q to %q.
2630-
`
2631-
2632-
const outputBackendMigrateLocal = `
2633-
Terraform has detected you're unconfiguring your previously set %q backend.
2634-
`
2635-
2636-
const outputStateStoreMigrateLocal = `
2637-
Terraform has detected you're unconfiguring your previously set %q state store.
2638-
`
2639-
2640-
const outputBackendReconfigure = `
2641-
[reset][bold]Backend configuration changed![reset]
2642-
2643-
Terraform has detected that the configuration specified for the backend
2644-
has changed. Terraform will now check for existing state in the backends.
2645-
`
2646-
26472624
const inputCloudInitCreateWorkspace = `
26482625
There are no workspaces with the configured tags (%s)
26492626
in your HCP Terraform organization. To finish initializing, Terraform needs at
@@ -2652,12 +2629,3 @@ least one workspace available.
26522629
Terraform can create a properly tagged workspace for you now. Please enter a
26532630
name to create a new HCP Terraform workspace.
26542631
`
2655-
2656-
const successBackendUnset = `
2657-
Successfully unset the backend %q. Terraform will now operate locally.
2658-
`
2659-
2660-
const successBackendSet = `
2661-
Successfully configured the backend %q! Terraform will automatically
2662-
use this backend unless the backend configuration changes.
2663-
`

internal/command/views/init.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,46 @@ var MessageRegistry map[InitMessageCode]InitMessage = map[InitMessageCode]InitMe
263263
HumanValue: "[reset][green]\n\nSuccessfully unset the state store %q. Terraform will now operate locally.",
264264
JSONValue: "Successfully unset the state store %q. Terraform will now operate locally.",
265265
},
266+
"backend_configured_success": {
267+
HumanValue: backendConfiguredSuccessHuman,
268+
JSONValue: backendConfiguredSuccessJSON,
269+
},
270+
"backend_configured_unset": {
271+
HumanValue: backendConfiguredUnsetHuman,
272+
JSONValue: backendConfiguredUnsetJSON,
273+
},
274+
"backend_migrate_to_cloud": {
275+
HumanValue: "Migrating from backend %q to HCP Terraform.",
276+
JSONValue: "Migrating from backend %q to HCP Terraform.",
277+
},
278+
"backend_migrate_from_cloud": {
279+
HumanValue: "Migrating from HCP Terraform to backend %q.",
280+
JSONValue: "Migrating from HCP Terraform to backend %q.",
281+
},
282+
"backend_cloud_change_in_place": {
283+
HumanValue: "HCP Terraform configuration has changed.",
284+
JSONValue: "HCP Terraform configuration has changed.",
285+
},
286+
"backend_migrate_type_change": {
287+
HumanValue: backendMigrateTypeChangeHuman,
288+
JSONValue: backendMigrateTypeChangeJSON,
289+
},
290+
"backend_reconfigure": {
291+
HumanValue: backendReconfigureHuman,
292+
JSONValue: backendReconfigureJSON,
293+
},
294+
"backend_migrate_local": {
295+
HumanValue: backendMigrateLocalHuman,
296+
JSONValue: backendMigrateLocalJSON,
297+
},
298+
"backend_cloud_migrate_local": {
299+
HumanValue: "Migrating from HCP Terraform or Terraform Enterprise to local state.",
300+
JSONValue: "Migrating from HCP Terraform or Terraform Enterprise to local state.",
301+
},
302+
"state_store_migrate_local": {
303+
HumanValue: stateMigrateLocalHuman,
304+
JSONValue: stateMigrateLocalJSON,
305+
},
266306
"empty_message": {
267307
HumanValue: "",
268308
JSONValue: "",
@@ -303,6 +343,26 @@ const (
303343

304344
// InitConfigError indicates problems encountered during initialisation
305345
InitConfigError InitMessageCode = "init_config_error"
346+
// BackendConfiguredSuccessMessage indicates successful backend configuration
347+
BackendConfiguredSuccessMessage InitMessageCode = "backend_configured_success"
348+
// BackendConfiguredUnsetMessage indicates successful backend unsetting
349+
BackendConfiguredUnsetMessage InitMessageCode = "backend_configured_unset"
350+
// BackendMigrateToCloudMessage indicates migration to HCP Terraform
351+
BackendMigrateToCloudMessage InitMessageCode = "backend_migrate_to_cloud"
352+
// BackendMigrateFromCloudMessage indicates migration from HCP Terraform
353+
BackendMigrateFromCloudMessage InitMessageCode = "backend_migrate_from_cloud"
354+
// BackendCloudChangeInPlaceMessage indicates HCP Terraform configuration change
355+
BackendCloudChangeInPlaceMessage InitMessageCode = "backend_cloud_change_in_place"
356+
// BackendMigrateTypeChangeMessage indicates backend type change
357+
BackendMigrateTypeChangeMessage InitMessageCode = "backend_migrate_type_change"
358+
// BackendReconfigureMessage indicates backend reconfiguration
359+
BackendReconfigureMessage InitMessageCode = "backend_reconfigure"
360+
// BackendMigrateLocalMessage indicates migration to local backend
361+
BackendMigrateLocalMessage InitMessageCode = "backend_migrate_local"
362+
// BackendCloudMigrateLocalMessage indicates migration from cloud to local
363+
BackendCloudMigrateLocalMessage InitMessageCode = "backend_cloud_migrate_local"
364+
// StateMigrateLocalMessage indicates migration from state store to local
365+
StateMigrateLocalMessage InitMessageCode = "state_store_migrate_local"
306366
// FindingMatchingVersionMessage indicates that Terraform is looking for a provider version that matches the constraint during installation
307367
FindingMatchingVersionMessage InitMessageCode = "finding_matching_version_message"
308368
// InstalledProviderVersionInfo describes a successfully installed provider along with its version
@@ -434,3 +494,40 @@ with the configuration, described below.
434494
The Terraform configuration must be valid before initialization so that
435495
Terraform can determine which modules and providers need to be installed.
436496
`
497+
498+
const backendConfiguredSuccessHuman = `[reset][green]
499+
Successfully configured the backend %q! Terraform will automatically
500+
use this backend unless the backend configuration changes.`
501+
502+
const backendConfiguredSuccessJSON = `Successfully configured the backend %q! Terraform will automatically
503+
use this backend unless the backend configuration changes.`
504+
505+
const backendConfiguredUnsetHuman = `[reset][green]
506+
507+
Successfully unset the backend %q. Terraform will now operate locally.`
508+
509+
const backendConfiguredUnsetJSON = `Successfully unset the backend %q. Terraform will now operate locally.`
510+
511+
const backendMigrateTypeChangeHuman = `[reset]Terraform detected that the backend type changed from %q to %q.
512+
`
513+
514+
const backendMigrateTypeChangeJSON = `Terraform detected that the backend type changed from %q to %q.`
515+
516+
const backendReconfigureHuman = `[reset][bold]Backend configuration changed![reset]
517+
518+
Terraform has detected that the configuration specified for the backend
519+
has changed. Terraform will now check for existing state in the backends.
520+
`
521+
522+
const backendReconfigureJSON = `Backend configuration changed!
523+
524+
Terraform has detected that the configuration specified for the backend
525+
has changed. Terraform will now check for existing state in the backends.`
526+
527+
const backendMigrateLocalHuman = `Terraform has detected you're unconfiguring your previously set %q backend.`
528+
529+
const backendMigrateLocalJSON = `Terraform has detected you're unconfiguring your previously set %q backend.`
530+
531+
const stateMigrateLocalHuman = `Terraform has detected you're unconfiguring your previously set %q state store.`
532+
533+
const stateMigrateLocalJSON = `Terraform has detected you're unconfiguring your previously set %q state store.`

0 commit comments

Comments
 (0)