Skip to content
Merged
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-20260506-095857.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: BUG FIXES
body: Avoid printing warnings from 'terraform output -json'
time: 2026-05-06T09:58:57.334037+01:00
custom:
Issue: "38530"
83 changes: 83 additions & 0 deletions internal/command/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ package command

import (
"bytes"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/zclconf/go-cty/cty"

"github.com/hashicorp/terraform/internal/addrs"
Expand Down Expand Up @@ -463,3 +465,84 @@ 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).
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"}
code := c.Run(args)
output := done(t)
if code != 0 {
t.Fatalf("unexpected exit code %d\nstderr:\n%s", code, output.Stderr())
}

stderr := output.Stderr()
if strings.Contains(stderr, "deprecated") {
t.Fatalf("warnings should be suppressed, got:\n%s", stderr)
}

expectedJson := `{
"foo":{
"sensitive":false,
"type":"string",
"value":"bar"
}
}`
if diff := cmp.Diff(expectedJson, output.Stdout(), transformJSON); diff != "" {
t.Fatalf("unexpected output: %s", diff)
}
}

var transformJSON = cmp.FilterValues(func(x, y string) bool {
xBytes := []byte(x)
yBytes := []byte(y)
return json.Valid(xBytes) && json.Valid(yBytes)
}, cmp.Transformer("ParseJSON", func(in string) (out any) {
inBytes := []byte(in)
if err := json.Unmarshal(inBytes, &out); err != nil {
panic(err)
}
return out
}))
5 changes: 4 additions & 1 deletion internal/command/views/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,10 @@ 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 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