diff --git a/internal/command/format/diff.go b/internal/command/format/diff.go index b87b11c184..67aec91154 100644 --- a/internal/command/format/diff.go +++ b/internal/command/format/diff.go @@ -471,8 +471,28 @@ func (p *blockBodyDiffPrinter) writeAttrDiff(name string, attrS *configschema.At } if attrS.NestedType != nil { - p.writeNestedAttrDiff(name, attrS.NestedType, old, new, nameLen, indent, path, action, showJustNew) - return false + renderNested := true + + // If the collection values are empty or null, we render them as single attributes + switch attrS.NestedType.Nesting { + case configschema.NestingList, configschema.NestingSet, configschema.NestingMap: + var oldLen, newLen int + if !old.IsNull() && old.IsKnown() { + oldLen = old.LengthInt() + } + if !new.IsNull() && new.IsKnown() { + newLen = new.LengthInt() + } + + if oldLen+newLen == 0 { + renderNested = false + } + } + + if renderNested { + p.writeNestedAttrDiff(name, attrS.NestedType, old, new, nameLen, indent, path, action, showJustNew) + return false + } } p.buf.WriteString("\n") @@ -613,6 +633,7 @@ func (p *blockBodyDiffPrinter) writeNestedAttrDiff( allItems := make([]cty.Value, 0, len(oldItems)+len(newItems)) allItems = append(allItems, oldItems...) allItems = append(allItems, newItems...) + all := cty.SetVal(allItems) p.buf.WriteString(" = [") diff --git a/internal/command/format/diff_test.go b/internal/command/format/diff_test.go index 2b4ece8d2e..9775e7cbf5 100644 --- a/internal/command/format/diff_test.go +++ b/internal/command/format/diff_test.go @@ -2861,6 +2861,41 @@ func TestResourceChange_nestedSet(t *testing.T) { - volume_type = "gp2" -> null } } +`, + }, + "in-place update - empty nested sets": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), + "root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "volume_type": cty.String, + })), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), + "root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "volume_type": cty.String, + })), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchema(configschema.NestingSet), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + + disks = [] + id = "i-02ae66f368e8518a9" + } `, }, }