Skip to content

Commit aee13f7

Browse files
committed
plans: 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 that resource B will be destroyed because it does not exist in configuration, without explicitly connecting this to 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 1347aa2 commit aee13f7

File tree

9 files changed

+53
-18
lines changed

9 files changed

+53
-18
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
@@ -413,6 +413,8 @@ func (p *plan) marshalResourceChanges(resources []*plans.ResourceInstanceChangeS
413413
r.ActionReason = "delete_because_each_key"
414414
case plans.ResourceInstanceDeleteBecauseNoModule:
415415
r.ActionReason = "delete_because_no_module"
416+
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
417+
r.ActionReason = "delete_because_no_move_target"
416418
case plans.ResourceInstanceReadBecauseConfigUnknown:
417419
r.ActionReason = "read_because_config_unknown"
418420
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: 12 additions & 6 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
@@ -151,6 +151,7 @@ enum ResourceInstanceActionReason {
151151
REPLACE_BY_TRIGGERS = 9;
152152
READ_BECAUSE_CONFIG_UNKNOWN = 10;
153153
READ_BECAUSE_DEPENDENCY_PENDING = 11;
154+
DELETE_BECAUSE_NO_MOVE_TARGET = 12;
154155
}
155156

156157
message ResourceInstanceChange {

internal/plans/planfile/tfplan.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ func resourceChangeFromTfplan(rawChange *planproto.ResourceInstanceChange) (*pla
331331
ret.ActionReason = plans.ResourceInstanceReadBecauseConfigUnknown
332332
case planproto.ResourceInstanceActionReason_READ_BECAUSE_DEPENDENCY_PENDING:
333333
ret.ActionReason = plans.ResourceInstanceReadBecauseDependencyPending
334+
case planproto.ResourceInstanceActionReason_DELETE_BECAUSE_NO_MOVE_TARGET:
335+
ret.ActionReason = plans.ResourceInstanceDeleteBecauseNoMoveTarget
334336
default:
335337
return nil, fmt.Errorf("resource has invalid action reason %s", rawChange.ActionReason)
336338
}
@@ -705,6 +707,8 @@ func resourceChangeToTfplan(change *plans.ResourceInstanceChangeSrc) (*planproto
705707
ret.ActionReason = planproto.ResourceInstanceActionReason_READ_BECAUSE_CONFIG_UNKNOWN
706708
case plans.ResourceInstanceReadBecauseDependencyPending:
707709
ret.ActionReason = planproto.ResourceInstanceActionReason_READ_BECAUSE_DEPENDENCY_PENDING
710+
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
711+
ret.ActionReason = planproto.ResourceInstanceActionReason_DELETE_BECAUSE_NO_MOVE_TARGET
708712
default:
709713
return nil, fmt.Errorf("resource %s has unsupported action reason %s", change.Addr, change.ActionReason)
710714
}

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)