Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/v1.15/BUG FIXES-20251119-103000.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: BUG FIXES
body: 'cli: Fixed `terraform init -json` to properly format all backend configuration messages as JSON instead of plain text'
time: 2025-11-19T10:30:00.000000Z
custom:
Issue: "37911"
64 changes: 16 additions & 48 deletions internal/command/meta_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -1132,10 +1132,11 @@ func (m *Meta) backend_c_r_S(
// Get the backend type for output
backendType := s.Backend.Type

view := views.NewInit(vt, m.View)
if cloudMode == cloud.ConfigMigrationOut {
m.Ui.Output("Migrating from HCP Terraform or Terraform Enterprise to local state.")
view.Output(views.BackendCloudMigrateLocalMessage)
} else {
m.Ui.Output(fmt.Sprintf(strings.TrimSpace(outputBackendMigrateLocal), s.Backend.Type))
view.Output(views.BackendMigrateLocalMessage, s.Backend.Type)
}

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

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

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

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

if output {
// Notify the user
view := views.NewInit(vt, m.View)
switch cloudMode {
case cloud.ConfigChangeInPlace:
m.Ui.Output("HCP Terraform configuration has changed.")
view.Output(views.BackendCloudChangeInPlaceMessage)
case cloud.ConfigMigrationIn:
m.Ui.Output(fmt.Sprintf("Migrating from backend %q to HCP Terraform.", s.Backend.Type))
view.Output(views.BackendMigrateToCloudMessage, s.Backend.Type)
case cloud.ConfigMigrationOut:
m.Ui.Output(fmt.Sprintf("Migrating from HCP Terraform to backend %q.", c.Type))
view.Output(views.BackendMigrateFromCloudMessage, c.Type)
default:
if s.Backend.Type != c.Type {
output := fmt.Sprintf(outputBackendMigrateChange, s.Backend.Type, c.Type)
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
"[reset]%s\n",
strings.TrimSpace(output))))
view.Output(views.BackendMigrateTypeChangeMessage, s.Backend.Type, c.Type)
} else {
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
"[reset]%s\n",
strings.TrimSpace(outputBackendReconfigure))))
view.Output(views.BackendReconfigureMessage)
}
}
}
Expand Down Expand Up @@ -1479,8 +1474,8 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista
// By now the backend is successfully configured. If using HCP Terraform, the success
// message is handled as part of the final init message
if _, ok := b.(*cloud.Cloud); !ok {
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
view := views.NewInit(vt, m.View)
view.Output(views.BackendConfiguredSuccessMessage, s.Backend.Type)
}
}

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

m.Ui.Output(fmt.Sprintf(strings.TrimSpace(outputStateStoreMigrateLocal), stateStoreType))
view := views.NewInit(viewType, m.View)
view.Output(views.StateMigrateLocalMessage, stateStoreType)

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

const outputBackendMigrateChange = `
Terraform detected that the backend type changed from %q to %q.
`

const outputBackendMigrateLocal = `
Terraform has detected you're unconfiguring your previously set %q backend.
`

const outputStateStoreMigrateLocal = `
Terraform has detected you're unconfiguring your previously set %q state store.
`

const outputBackendReconfigure = `
[reset][bold]Backend configuration changed![reset]

Terraform has detected that the configuration specified for the backend
has changed. Terraform will now check for existing state in the backends.
`

const inputCloudInitCreateWorkspace = `
There are no workspaces with the configured tags (%s)
in your HCP Terraform organization. To finish initializing, Terraform needs at
Expand All @@ -2652,12 +2629,3 @@ least one workspace available.
Terraform can create a properly tagged workspace for you now. Please enter a
name to create a new HCP Terraform workspace.
`

const successBackendUnset = `
Successfully unset the backend %q. Terraform will now operate locally.
`

const successBackendSet = `
Successfully configured the backend %q! Terraform will automatically
use this backend unless the backend configuration changes.
`
97 changes: 97 additions & 0 deletions internal/command/views/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,46 @@ var MessageRegistry map[InitMessageCode]InitMessage = map[InitMessageCode]InitMe
HumanValue: "[reset][green]\n\nSuccessfully unset the state store %q. Terraform will now operate locally.",
JSONValue: "Successfully unset the state store %q. Terraform will now operate locally.",
},
"backend_configured_success": {
HumanValue: backendConfiguredSuccessHuman,
JSONValue: backendConfiguredSuccessJSON,
},
"backend_configured_unset": {
HumanValue: backendConfiguredUnsetHuman,
JSONValue: backendConfiguredUnsetJSON,
},
"backend_migrate_to_cloud": {
HumanValue: "Migrating from backend %q to HCP Terraform.",
JSONValue: "Migrating from backend %q to HCP Terraform.",
},
"backend_migrate_from_cloud": {
HumanValue: "Migrating from HCP Terraform to backend %q.",
JSONValue: "Migrating from HCP Terraform to backend %q.",
},
"backend_cloud_change_in_place": {
HumanValue: "HCP Terraform configuration has changed.",
JSONValue: "HCP Terraform configuration has changed.",
},
"backend_migrate_type_change": {
HumanValue: backendMigrateTypeChangeHuman,
JSONValue: backendMigrateTypeChangeJSON,
},
"backend_reconfigure": {
HumanValue: backendReconfigureHuman,
JSONValue: backendReconfigureJSON,
},
"backend_migrate_local": {
HumanValue: backendMigrateLocalHuman,
JSONValue: backendMigrateLocalJSON,
},
"backend_cloud_migrate_local": {
HumanValue: "Migrating from HCP Terraform or Terraform Enterprise to local state.",
JSONValue: "Migrating from HCP Terraform or Terraform Enterprise to local state.",
},
"state_store_migrate_local": {
HumanValue: stateMigrateLocalHuman,
JSONValue: stateMigrateLocalJSON,
},
"empty_message": {
HumanValue: "",
JSONValue: "",
Expand Down Expand Up @@ -302,6 +342,26 @@ const (

// InitConfigError indicates problems encountered during initialisation
InitConfigError InitMessageCode = "init_config_error"
// BackendConfiguredSuccessMessage indicates successful backend configuration
BackendConfiguredSuccessMessage InitMessageCode = "backend_configured_success"
// BackendConfiguredUnsetMessage indicates successful backend unsetting
BackendConfiguredUnsetMessage InitMessageCode = "backend_configured_unset"
// BackendMigrateToCloudMessage indicates migration to HCP Terraform
BackendMigrateToCloudMessage InitMessageCode = "backend_migrate_to_cloud"
// BackendMigrateFromCloudMessage indicates migration from HCP Terraform
BackendMigrateFromCloudMessage InitMessageCode = "backend_migrate_from_cloud"
// BackendCloudChangeInPlaceMessage indicates HCP Terraform configuration change
BackendCloudChangeInPlaceMessage InitMessageCode = "backend_cloud_change_in_place"
// BackendMigrateTypeChangeMessage indicates backend type change
BackendMigrateTypeChangeMessage InitMessageCode = "backend_migrate_type_change"
// BackendReconfigureMessage indicates backend reconfiguration
BackendReconfigureMessage InitMessageCode = "backend_reconfigure"
// BackendMigrateLocalMessage indicates migration to local backend
BackendMigrateLocalMessage InitMessageCode = "backend_migrate_local"
// BackendCloudMigrateLocalMessage indicates migration from cloud to local
BackendCloudMigrateLocalMessage InitMessageCode = "backend_cloud_migrate_local"
// StateMigrateLocalMessage indicates migration from state store to local
StateMigrateLocalMessage InitMessageCode = "state_store_migrate_local"
// FindingMatchingVersionMessage indicates that Terraform is looking for a provider version that matches the constraint during installation
FindingMatchingVersionMessage InitMessageCode = "finding_matching_version_message"
// InstalledProviderVersionInfo describes a successfully installed provider along with its version
Expand Down Expand Up @@ -433,3 +493,40 @@ with the configuration, described below.
The Terraform configuration must be valid before initialization so that
Terraform can determine which modules and providers need to be installed.
`

const backendConfiguredSuccessHuman = `[reset][green]
Successfully configured the backend %q! Terraform will automatically
use this backend unless the backend configuration changes.`

const backendConfiguredSuccessJSON = `Successfully configured the backend %q! Terraform will automatically
use this backend unless the backend configuration changes.`

const backendConfiguredUnsetHuman = `[reset][green]

Successfully unset the backend %q. Terraform will now operate locally.`

const backendConfiguredUnsetJSON = `Successfully unset the backend %q. Terraform will now operate locally.`

const backendMigrateTypeChangeHuman = `[reset]Terraform detected that the backend type changed from %q to %q.
`

const backendMigrateTypeChangeJSON = `Terraform detected that the backend type changed from %q to %q.`

const backendReconfigureHuman = `[reset][bold]Backend configuration changed![reset]

Terraform has detected that the configuration specified for the backend
has changed. Terraform will now check for existing state in the backends.
`

const backendReconfigureJSON = `Backend configuration changed!

Terraform has detected that the configuration specified for the backend
has changed. Terraform will now check for existing state in the backends.`

const backendMigrateLocalHuman = `Terraform has detected you're unconfiguring your previously set %q backend.`

const backendMigrateLocalJSON = `Terraform has detected you're unconfiguring your previously set %q backend.`

const stateMigrateLocalHuman = `Terraform has detected you're unconfiguring your previously set %q state store.`

const stateMigrateLocalJSON = `Terraform has detected you're unconfiguring your previously set %q state store.`