diff --git a/terraform/node_output.go b/terraform/node_output.go index 84fb67408e..00f39c80e3 100644 --- a/terraform/node_output.go +++ b/terraform/node_output.go @@ -358,11 +358,13 @@ func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) error // if this is a root module, try to get a before value from the state for // the diff + sensitiveBefore := false before := cty.NullVal(cty.DynamicPseudoType) mod := state.Module(n.Addr.Module) if n.Addr.Module.IsRoot() && mod != nil { for name, o := range mod.OutputValues { if name == n.Addr.OutputValue.Name { + sensitiveBefore = o.Sensitive before = o.Value break } @@ -372,7 +374,8 @@ func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) error changes := ctx.Changes() if changes != nil { change := &plans.OutputChange{ - Addr: n.Addr, + Addr: n.Addr, + Sensitive: sensitiveBefore, Change: plans.Change{ Action: plans.Delete, Before: before, @@ -413,32 +416,51 @@ func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.C if changes != nil { // if this is a root module, try to get a before value from the state for // the diff + sensitiveBefore := false before := cty.NullVal(cty.DynamicPseudoType) mod := state.Module(n.Addr.Module) if n.Addr.Module.IsRoot() && mod != nil { for name, o := range mod.OutputValues { if name == n.Addr.OutputValue.Name { before = o.Value + sensitiveBefore = o.Sensitive break } } } + // We will not show the value is either the before or after are marked + // as sensitivity. We can show the value again once sensitivity is + // removed from both the config and the state. + sensitiveChange := sensitiveBefore || n.Config.Sensitive + + // strip any marks here just to be sure we don't panic on the True comparison + val, _ = val.UnmarkDeep() + var action plans.Action switch { case val.IsNull(): action = plans.Delete + case before.IsNull(): action = plans.Create - case val.IsWhollyKnown() && val.Equals(before).True(): + + case val.IsWhollyKnown() && + val.Equals(before).True() && + n.Config.Sensitive == sensitiveBefore: + // Sensitivity must also match to be a NoOp. + // Theoretically marks may not match here, but sensitivity is the + // only one we can act on, and the state will have been loaded + // without any marks to consider. action = plans.NoOp + default: action = plans.Update } change := &plans.OutputChange{ Addr: n.Addr, - Sensitive: n.Config.Sensitive, + Sensitive: sensitiveChange, Change: plans.Change{ Action: action, Before: before, diff --git a/terraform/transform_output.go b/terraform/transform_output.go index 964a3a898b..1e40f9edd5 100644 --- a/terraform/transform_output.go +++ b/terraform/transform_output.go @@ -47,8 +47,6 @@ func (t *OutputTransformer) transform(g *Graph, c *configs.Config) error { // into NodeApplyableOutputs to reflect possible expansion // through the presence of "count" or "for_each" on the modules. - // if this is a root output, we add the apply or destroy node directly, as - // the root modules does not expand var changes []*plans.OutputChangeSrc if t.Changes != nil { changes = t.Changes.Outputs @@ -69,6 +67,9 @@ func (t *OutputTransformer) transform(g *Graph, c *configs.Config) error { destroy = rootChange.Action == plans.Delete } + // If this is a root output, we add the apply or destroy node directly, + // as the root modules does not expand. + var node dag.Vertex switch { case c.Path.IsRoot() && destroy: