@@ -261,17 +261,6 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
261261e .mut .Lock ()
262262defer e .mut .Unlock ()
263263
264- // TODO: defunct?
265- // var isPrebuild bool
266- // for _, v := range env {
267- // if envName(v) == provider.IsPrebuildEnvironmentVariable() && envVar(v) == "true" {
268- // isPrebuild = true
269- // break
270- // }
271- // }
272-
273- // _ = isPrebuild
274-
275264planfilePath := getPlanFilePath (e .workdir )
276265args := []string {
277266"plan" ,
@@ -341,6 +330,68 @@ func onlyDataResources(sm tfjson.StateModule) tfjson.StateModule {
341330return filtered
342331}
343332
333+ func (e * executor )logResourceReplacements (ctx context.Context ,plan * tfjson.Plan ) {
334+ if plan == nil {
335+ return
336+ }
337+
338+ if len (plan .ResourceChanges )== 0 {
339+ return
340+ }
341+ var (
342+ count int
343+ replacements = make (map [string ][]string ,len (plan .ResourceChanges ))
344+ )
345+
346+ for _ ,ch := range plan .ResourceChanges {
347+ // No change, no problem!
348+ if ch .Change == nil {
349+ continue
350+ }
351+
352+ // No-op change, no problem!
353+ if ch .Change .Actions .NoOp () {
354+ continue
355+ }
356+
357+ // No replacements, no problem!
358+ if len (ch .Change .ReplacePaths )== 0 {
359+ continue
360+ }
361+
362+ // Replacing our resources, no problem!
363+ if strings .Index (ch .Type ,"coder_" )== 0 {
364+ continue
365+ }
366+
367+ for _ ,p := range ch .Change .ReplacePaths {
368+ var path string
369+ switch p := p .(type ) {
370+ case []interface {}:
371+ segs := p
372+ list := make ([]string ,0 ,len (segs ))
373+ for _ ,s := range segs {
374+ list = append (list ,fmt .Sprintf ("%v" ,s ))
375+ }
376+ path = strings .Join (list ,"." )
377+ default :
378+ path = fmt .Sprintf ("%v" ,p )
379+ }
380+
381+ replacements [ch .Address ]= append (replacements [ch .Address ],path )
382+ }
383+
384+ count ++
385+ }
386+
387+ if count > 0 {
388+ e .server .logger .Warn (ctx ,"plan introduces resource changes" ,slog .F ("count" ,count ))
389+ for n ,p := range replacements {
390+ e .server .logger .Warn (ctx ,"resource will be replaced!" ,slog .F ("name" ,n ),slog .F ("replacement_paths" ,strings .Join (p ,"," )))
391+ }
392+ }
393+ }
394+
344395// planResources must only be called while the lock is held.
345396func (e * executor )planResources (ctx ,killCtx context.Context ,planfilePath string ) (* State , json.RawMessage ,error ) {
346397ctx ,span := e .server .startTrace (ctx ,tracing .FuncName ())
@@ -351,6 +402,8 @@ func (e *executor) planResources(ctx, killCtx context.Context, planfilePath stri
351402return nil ,nil ,xerrors .Errorf ("show terraform plan file: %w" ,err )
352403}
353404
405+ e .logResourceReplacements (ctx ,plan )
406+
354407rawGraph ,err := e .graph (ctx ,killCtx )
355408if err != nil {
356409return nil ,nil ,xerrors .Errorf ("graph: %w" ,err )