Skip to content
Closed
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.16/BUG FIXES-20260504-160000.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: BUG FIXES
body: Avoid warnings on stdout in 'terraform output -json' so the result remains parseable JSON
time: 2026-05-04T16:00:00.000000+00:00
custom:
Issue: "38512"
74 changes: 74 additions & 0 deletions internal/command/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,3 +463,77 @@ func TestOutputRaw_warningsSuppressed(t *testing.T) {
t.Fatalf("expected output \"bar\", got: %#v", actual)
}
}

func TestOutputJSON_warningsSuppressed(t *testing.T) {
// Pre-populate the inmem backend with a state containing an output value
inmem.Reset()
originalState := states.BuildState(func(s *states.SyncState) {
s.SetOutputValue(
addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance),
cty.StringVal("bar"),
false,
)
})

// Register a backend that wraps inmem with a deprecation warning,
// simulating a backend like S3 whose PrepareConfig warns about
// deprecated attributes (e.g. dynamodb_table). The same fixture
// powering TestOutputRaw_warningsSuppressed also reproduces the
// `-json` issue reported in #38512.
backendInit.Set("inmem", func() backend.Backend {
return &deprecatedInmemBackend{Backend: inmem.New()}
})
defer backendInit.Set("inmem", inmem.New)

td := t.TempDir()
testCopyDir(t, testFixturePath("output-backend-with-deprecation"), td)
t.Chdir(td)

// Write the state into the inmem backend's default workspace
b := inmem.New()
b.Configure(cty.ObjectVal(map[string]cty.Value{
"lock_id": cty.NullVal(cty.String),
}))
sMgr, sDiags := b.StateMgr(backend.DefaultStateName)
if sDiags.HasErrors() {
t.Fatalf("unexpected error: %s", sDiags.Err())
}
sMgr.WriteState(originalState)
if err := sMgr.PersistState(nil); err != nil {
t.Fatalf("unexpected error: %s", err)
}

view, done := testView(t)
c := &OutputCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
View: view,
},
}

args := []string{"-json", "foo"}
code := c.Run(args)
output := done(t)
if code != 0 {
t.Fatalf("unexpected exit code %d\nstderr:\n%s", code, output.Stderr())
}

// The key assertion: warnings must not appear on stdout because the
// caller is piping the output to `jq` or similar and any non-JSON
// content there breaks that pipeline. They are also not expected on
// stderr in this run because there was no error to surface.
stdout := output.Stdout()
if strings.Contains(stdout, "deprecated") {
t.Fatalf("warnings should be suppressed from stdout in -json mode, got:\n%s", stdout)
}
stderr := output.Stderr()
if strings.Contains(stderr, "deprecated") {
t.Fatalf("warnings should be suppressed in -json mode, got on stderr:\n%s", stderr)
}

// What remains on stdout must be parseable JSON containing the value.
actual := strings.TrimSpace(stdout)
if actual != `"bar"` {
t.Fatalf(`expected output "\"bar\"", got: %#v`, actual)
}
}
8 changes: 7 additions & 1 deletion internal/command/views/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,13 @@ func (v *OutputJSON) Output(name string, outputs map[string]*states.OutputValue)
}

func (v *OutputJSON) Diagnostics(diags tfdiags.Diagnostics) {
v.view.Diagnostics(diags)
// filter out warnings as these wouldn't be expected in JSON mode
// either: pipelines like `terraform output -json | jq` cannot
// tolerate non-JSON content on stdout, and warnings typically don't
// influence the exit code so the user cannot expect them in stdout.
// Mirrors the same suppression added for `-raw` in #38487.
Comment on lines +261 to +265
Copy link
Copy Markdown
Member

@radeksimko radeksimko May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// filter out warnings as these wouldn't be expected in JSON mode
// either: pipelines like `terraform output -json | jq` cannot
// tolerate non-JSON content on stdout, and warnings typically don't
// influence the exit code so the user cannot expect them in stdout.
// Mirrors the same suppression added for `-raw` in #38487.
// filter out warnings as we cannot change the current format
// to introduce them gracefully (without breaking existing usage)

errsOnly := diags.ErrorsOnly()
v.view.Diagnostics(errsOnly)
}

// For text and raw output modes, an empty map of outputs is considered a
Expand Down
Loading