@@ -12,7 +12,6 @@ import (
1212 "github.com/hashicorp/terraform/internal/plans"
1313 "github.com/hashicorp/terraform/internal/plans/internal/planproto"
1414 "github.com/hashicorp/terraform/internal/states"
15- "github.com/hashicorp/terraform/internal/tfdiags"
1615 "github.com/hashicorp/terraform/version"
1716 "github.com/zclconf/go-cty/cty"
1817)
@@ -157,12 +156,23 @@ func resourceChangeFromTfplan(rawChange *planproto.ResourceInstanceChange) (*pla
157156
158157 ret := & plans.ResourceInstanceChangeSrc {}
159158
160- moduleAddr := addrs .RootModuleInstance
161- if rawChange .ModulePath != "" {
162- var diags tfdiags.Diagnostics
163- moduleAddr , diags = addrs .ParseModuleInstanceStr (rawChange .ModulePath )
159+ if rawChange .Addr == "" {
160+ // If "Addr" isn't populated then seems likely that this is a plan
161+ // file created by an earlier version of Terraform, which had the
162+ // same information spread over various other fields:
163+ // ModulePath, Mode, Name, Type, and InstanceKey.
164+ return nil , fmt .Errorf ("no instance address for resource instance change; perhaps this plan was created by a different version of Terraform?" )
165+ }
166+
167+ instAddr , diags := addrs .ParseAbsResourceInstanceStr (rawChange .Addr )
168+ if diags .HasErrors () {
169+ return nil , fmt .Errorf ("invalid resource instance address %q: %w" , rawChange .Addr , diags .Err ())
170+ }
171+ prevRunAddr := instAddr
172+ if rawChange .PrevRunAddr != "" {
173+ prevRunAddr , diags = addrs .ParseAbsResourceInstanceStr (rawChange .PrevRunAddr )
164174 if diags .HasErrors () {
165- return nil , diags .Err ()
175+ return nil , fmt . Errorf ( "invalid resource instance previous run address %q: %w" , rawChange . PrevRunAddr , diags .Err () )
166176 }
167177 }
168178
@@ -172,37 +182,8 @@ func resourceChangeFromTfplan(rawChange *planproto.ResourceInstanceChange) (*pla
172182 }
173183 ret .ProviderAddr = providerAddr
174184
175- var mode addrs.ResourceMode
176- switch rawChange .Mode {
177- case planproto .ResourceInstanceChange_managed :
178- mode = addrs .ManagedResourceMode
179- case planproto .ResourceInstanceChange_data :
180- mode = addrs .DataResourceMode
181- default :
182- return nil , fmt .Errorf ("resource has invalid mode %s" , rawChange .Mode )
183- }
184-
185- typeName := rawChange .Type
186- name := rawChange .Name
187-
188- resAddr := addrs.Resource {
189- Mode : mode ,
190- Type : typeName ,
191- Name : name ,
192- }
193-
194- var instKey addrs.InstanceKey
195- switch rawTk := rawChange .InstanceKey .(type ) {
196- case nil :
197- case * planproto.ResourceInstanceChange_Int :
198- instKey = addrs .IntKey (rawTk .Int )
199- case * planproto.ResourceInstanceChange_Str :
200- instKey = addrs .StringKey (rawTk .Str )
201- default :
202- return nil , fmt .Errorf ("instance of %s has invalid key type %T" , resAddr .Absolute (moduleAddr ), rawChange .InstanceKey )
203- }
204-
205- ret .Addr = resAddr .Instance (instKey ).Absolute (moduleAddr )
185+ ret .Addr = instAddr
186+ ret .PrevRunAddr = prevRunAddr
206187
207188 if rawChange .DeposedKey != "" {
208189 if len (rawChange .DeposedKey ) != 8 {
@@ -454,35 +435,20 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error {
454435func resourceChangeToTfplan (change * plans.ResourceInstanceChangeSrc ) (* planproto.ResourceInstanceChange , error ) {
455436 ret := & planproto.ResourceInstanceChange {}
456437
457- ret .ModulePath = change .Addr .Module .String ()
458-
459- relAddr := change .Addr .Resource
460-
461- switch relAddr .Resource .Mode {
462- case addrs .ManagedResourceMode :
463- ret .Mode = planproto .ResourceInstanceChange_managed
464- case addrs .DataResourceMode :
465- ret .Mode = planproto .ResourceInstanceChange_data
466- default :
467- return nil , fmt .Errorf ("resource %s has unsupported mode %s" , relAddr , relAddr .Resource .Mode )
438+ if change .PrevRunAddr .Resource .Resource .Type == "" {
439+ // Suggests that an old caller wasn't yet updated to populate this
440+ // properly. All code that generates plans should populate this field,
441+ // even if it's just to write in the same value as in change.Addr.
442+ change .PrevRunAddr = change .Addr
468443 }
469444
470- ret .Type = relAddr .Resource .Type
471- ret .Name = relAddr .Resource .Name
472-
473- switch tk := relAddr .Key .(type ) {
474- case nil :
475- // Nothing to do, then.
476- case addrs.IntKey :
477- ret .InstanceKey = & planproto.ResourceInstanceChange_Int {
478- Int : int64 (tk ),
479- }
480- case addrs.StringKey :
481- ret .InstanceKey = & planproto.ResourceInstanceChange_Str {
482- Str : string (tk ),
483- }
484- default :
485- return nil , fmt .Errorf ("resource %s has unsupported instance key type %T" , relAddr , relAddr .Key )
445+ ret .Addr = change .Addr .String ()
446+ ret .PrevRunAddr = change .PrevRunAddr .String ()
447+ if ret .PrevRunAddr == ret .Addr {
448+ // In the on-disk format we leave PrevRunAddr unpopulated in the common
449+ // case where it's the same as Addr, and then fill it back in again on
450+ // read.
451+ ret .PrevRunAddr = ""
486452 }
487453
488454 ret .DeposedKey = string (change .DeposedKey )
@@ -500,7 +466,7 @@ func resourceChangeToTfplan(change *plans.ResourceInstanceChangeSrc) (*planproto
500466
501467 valChange , err := changeToTfplan (& change .ChangeSrc )
502468 if err != nil {
503- return nil , fmt .Errorf ("failed to serialize resource %s change: %s" , relAddr , err )
469+ return nil , fmt .Errorf ("failed to serialize resource %s change: %s" , change . Addr , err )
504470 }
505471 ret .Change = valChange
506472
@@ -514,7 +480,7 @@ func resourceChangeToTfplan(change *plans.ResourceInstanceChangeSrc) (*planproto
514480 case plans .ResourceInstanceReplaceByRequest :
515481 ret .ActionReason = planproto .ResourceInstanceActionReason_REPLACE_BY_REQUEST
516482 default :
517- return nil , fmt .Errorf ("resource %s has unsupported action reason %s" , relAddr , change .ActionReason )
483+ return nil , fmt .Errorf ("resource %s has unsupported action reason %s" , change . Addr , change .ActionReason )
518484 }
519485
520486 if len (change .Private ) > 0 {
0 commit comments