Skip to content

Commit 3081083

Browse files
committed
do not create delete change for nonexistent output
If there are outputs in configuration, a destroy plan will always contain a "delete" change for each of these outputs. This leads to meaningless delete changes being present for outputs which were not present in state and therefore cannot be deleted. Since there is a change in the plan, this plan will then be considered applyable, and the user will be presented with text instructing them to apply a plan in which there are no actual changes. This commit stops the above from happening in the case of root module outputs.
1 parent 7b4a551 commit 3081083

File tree

2 files changed

+31
-8
lines changed

2 files changed

+31
-8
lines changed

internal/terraform/node_output.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"log"
66

77
"github.com/hashicorp/hcl/v2"
8+
"github.com/zclconf/go-cty/cty"
9+
810
"github.com/hashicorp/terraform/internal/addrs"
911
"github.com/hashicorp/terraform/internal/configs"
1012
"github.com/hashicorp/terraform/internal/dag"
@@ -13,7 +15,6 @@ import (
1315
"github.com/hashicorp/terraform/internal/plans"
1416
"github.com/hashicorp/terraform/internal/states"
1517
"github.com/hashicorp/terraform/internal/tfdiags"
16-
"github.com/zclconf/go-cty/cty"
1718
)
1819

1920
// nodeExpandOutput is the placeholder for a non-root module output that has
@@ -413,12 +414,14 @@ func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) tfdia
413414
before := cty.NullVal(cty.DynamicPseudoType)
414415
mod := state.Module(n.Addr.Module)
415416
if n.Addr.Module.IsRoot() && mod != nil {
416-
for name, o := range mod.OutputValues {
417-
if name == n.Addr.OutputValue.Name {
418-
sensitiveBefore = o.Sensitive
419-
before = o.Value
420-
break
421-
}
417+
if o, ok := mod.OutputValues[n.Addr.OutputValue.Name]; ok {
418+
sensitiveBefore = o.Sensitive
419+
before = o.Value
420+
} else {
421+
// If the output was not in state, a delete change would
422+
// be meaningless, so exit early.
423+
return nil
424+
422425
}
423426
}
424427

internal/terraform/node_output_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import (
55
"testing"
66

77
"github.com/hashicorp/hcl/v2"
8+
"github.com/zclconf/go-cty/cty"
9+
810
"github.com/hashicorp/terraform/internal/addrs"
911
"github.com/hashicorp/terraform/internal/configs"
1012
"github.com/hashicorp/terraform/internal/lang/marks"
1113
"github.com/hashicorp/terraform/internal/states"
12-
"github.com/zclconf/go-cty/cty"
1314
)
1415

1516
func TestNodeApplyableOutputExecute_knownValue(t *testing.T) {
@@ -160,3 +161,22 @@ func TestNodeDestroyableOutputExecute(t *testing.T) {
160161
t.Fatal("Unexpected outputs in state after removal")
161162
}
162163
}
164+
165+
func TestNodeDestroyableOutputExecute_notInState(t *testing.T) {
166+
outputAddr := addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)
167+
168+
state := states.NewState()
169+
170+
ctx := &MockEvalContext{
171+
StateState: state.SyncWrapper(),
172+
}
173+
node := NodeDestroyableOutput{Addr: outputAddr}
174+
175+
diags := node.Execute(ctx, walkApply)
176+
if diags.HasErrors() {
177+
t.Fatalf("Unexpected error: %s", diags.Err())
178+
}
179+
if state.OutputValue(outputAddr) != nil {
180+
t.Fatal("Unexpected outputs in state after removal")
181+
}
182+
}

0 commit comments

Comments
 (0)