Skip to content

Commit 891b5d5

Browse files
committed
plan: indicate when resource deleted due to move
Add a new ChangeReason, ReasonDeleteBecauseNoMoveTarget, to provide better information in cases where a planned deletion is due to moving a resource to a target not in configuration. Consider a case in which a resource instance exists in state at address A, and the user adds a moved block to move A to address B. Whether by the user's intention or not, address B does not exist in configuration. Terraform combines the move from A to B, and the lack of configuration for B, into a single delete action for the (previously nonexistent) entity B. Prior to this commit, the Terraform plan will report only that resource B will be destroyed because it does not exist in configuration, with no mention of the move. This commit provides the user an additional clue as to what has happened, in a case in which Terraform has elided a user's action and inaction into one potentially destructive change.
1 parent 101d0d9 commit 891b5d5

File tree

11 files changed

+96
-66
lines changed

11 files changed

+96
-66
lines changed

internal/command/format/diff.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ func ResourceChange(
115115
switch change.ActionReason {
116116
case plans.ResourceInstanceDeleteBecauseNoResourceConfig:
117117
buf.WriteString(fmt.Sprintf("\n # (because %s is not in configuration)", addr.Resource.Resource))
118+
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
119+
buf.WriteString(fmt.Sprintf("\n # (because %s was moved to %s, which is not in configuration)", change.PrevRunAddr, addr.Resource.Resource))
118120
case plans.ResourceInstanceDeleteBecauseNoModule:
119121
// FIXME: Ideally we'd truncate addr.Module to reflect the earliest
120122
// step that doesn't exist, so it's clearer which call this refers

internal/command/jsonplan/plan.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@ func (p *plan) marshalResourceChanges(resources []*plans.ResourceInstanceChangeS
405405
r.ActionReason = "delete_because_each_key"
406406
case plans.ResourceInstanceDeleteBecauseNoModule:
407407
r.ActionReason = "delete_because_no_module"
408+
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
409+
r.ActionReason = "delete_because_no_move_target"
408410
case plans.ResourceInstanceReadBecauseConfigUnknown:
409411
r.ActionReason = "read_because_config_unknown"
410412
case plans.ResourceInstanceReadBecauseDependencyPending:

internal/command/views/json/change.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const (
8080
ReasonDeleteBecauseCountIndex ChangeReason = "delete_because_count_index"
8181
ReasonDeleteBecauseEachKey ChangeReason = "delete_because_each_key"
8282
ReasonDeleteBecauseNoModule ChangeReason = "delete_because_no_module"
83+
ReasonDeleteBecauseNoMoveTarget ChangeReason = "delete_because_no_move_target"
8384
ReasonReadBecauseConfigUnknown ChangeReason = "read_because_config_unknown"
8485
ReasonReadBecauseDependencyPending ChangeReason = "read_because_dependency_pending"
8586
)
@@ -108,6 +109,8 @@ func changeReason(reason plans.ResourceInstanceChangeActionReason) ChangeReason
108109
return ReasonDeleteBecauseNoModule
109110
case plans.ResourceInstanceReadBecauseConfigUnknown:
110111
return ReasonReadBecauseConfigUnknown
112+
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
113+
return ReasonDeleteBecauseNoMoveTarget
111114
case plans.ResourceInstanceReadBecauseDependencyPending:
112115
return ReasonReadBecauseDependencyPending
113116
default:

internal/plans/changes.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,12 @@ const (
427427
// specific reasons for a particular instance to no longer be declared.
428428
ResourceInstanceDeleteBecauseNoModule ResourceInstanceChangeActionReason = 'M'
429429

430+
// ResourceInstanceDeleteBecauseNoMoveTarget indicates that the resource
431+
// address appears as the target ("to") in a moved block, but no
432+
// configuration exists for that resource. According to our move rules,
433+
// this combination evaluates to a deletion of the "new" resource.
434+
ResourceInstanceDeleteBecauseNoMoveTarget ResourceInstanceChangeActionReason = 'A'
435+
430436
// ResourceInstanceReadBecauseConfigUnknown indicates that the resource
431437
// must be read during apply (rather than during planning) because its
432438
// configuration contains unknown values. This reason applies only to

internal/plans/internal/planproto/planfile.pb.go

Lines changed: 27 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/plans/internal/planproto/planfile.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ enum ResourceInstanceActionReason {
149149
REPLACE_BY_TRIGGERS = 9;
150150
READ_BECAUSE_CONFIG_UNKNOWN = 10;
151151
READ_BECAUSE_DEPENDENCY_PENDING = 11;
152+
DELETE_BECAUSE_NO_MOVE_TARGET = 12;
152153
}
153154

154155
message ResourceInstanceChange {

internal/plans/planfile/tfplan.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ func resourceChangeFromTfplan(rawChange *planproto.ResourceInstanceChange) (*pla
282282
ret.ActionReason = plans.ResourceInstanceReadBecauseConfigUnknown
283283
case planproto.ResourceInstanceActionReason_READ_BECAUSE_DEPENDENCY_PENDING:
284284
ret.ActionReason = plans.ResourceInstanceReadBecauseDependencyPending
285+
case planproto.ResourceInstanceActionReason_DELETE_BECAUSE_NO_MOVE_TARGET:
286+
ret.ActionReason = plans.ResourceInstanceDeleteBecauseNoMoveTarget
285287
default:
286288
return nil, fmt.Errorf("resource has invalid action reason %s", rawChange.ActionReason)
287289
}
@@ -633,6 +635,8 @@ func resourceChangeToTfplan(change *plans.ResourceInstanceChangeSrc) (*planproto
633635
ret.ActionReason = planproto.ResourceInstanceActionReason_READ_BECAUSE_CONFIG_UNKNOWN
634636
case plans.ResourceInstanceReadBecauseDependencyPending:
635637
ret.ActionReason = planproto.ResourceInstanceActionReason_READ_BECAUSE_DEPENDENCY_PENDING
638+
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
639+
ret.ActionReason = planproto.ResourceInstanceActionReason_DELETE_BECAUSE_NO_MOVE_TARGET
636640
default:
637641
return nil, fmt.Errorf("resource %s has unsupported action reason %s", change.Addr, change.ActionReason)
638642
}

internal/plans/resourceinstancechangeactionreason_string.go

Lines changed: 16 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/terraform/node_resource_plan_orphan.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalCon
157157
func (n *NodePlannableResourceInstanceOrphan) deleteActionReason(ctx EvalContext) plans.ResourceInstanceChangeActionReason {
158158
cfg := n.Config
159159
if cfg == nil {
160+
if !n.Addr.Equal(n.prevRunAddr(ctx)) {
161+
// This means the resource was moved - see also
162+
// ResourceInstanceChange.Moved() which calculates
163+
// this the same way.
164+
return plans.ResourceInstanceDeleteBecauseNoMoveTarget
165+
}
166+
160167
return plans.ResourceInstanceDeleteBecauseNoResourceConfig
161168
}
162169

0 commit comments

Comments
 (0)