Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions internal/generic/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ func (t toTerraform) valueFromRaw(ctx context.Context, schema typeAtTerraformPat
//
case []any:
if len(v) == 0 {
// Return an empty list/set instead of nil to preserve empty collections in state.
// This prevents perpetual diffs when the config has [] but AWS returns an empty array.
if typ.Is(tftypes.List{}) || typ.Is(tftypes.Set{}) || typ.Is(tftypes.Tuple{}) {
return tftypes.NewValue(typ, []tftypes.Value{}), nil
}
return tftypes.NewValue(typ, nil), nil
}

Expand Down Expand Up @@ -305,10 +310,15 @@ func (t toTerraform) valueFromRaw(ctx context.Context, schema typeAtTerraformPat
path = path.WithoutLastStep()
}
if isObject {
// Set any missing attributes to Null.
// Set any missing attributes to Null, or empty list/set for collection types.
// This prevents perpetual diffs when the config has [] but AWS omits the field.
for k, t := range typ.(tftypes.Object).AttributeTypes {
if _, ok := vals[k]; !ok {
vals[k] = tftypes.NewValue(t, nil)
if t.Is(tftypes.List{}) || t.Is(tftypes.Set{}) {
vals[k] = tftypes.NewValue(t, []tftypes.Value{})
} else {
vals[k] = tftypes.NewValue(t, nil)
}
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions internal/generic/translate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ func TestTranslateToTerraform(t *testing.T) {
"Number": float64(42),
"Ports": []any{},
},
// Empty lists from AWS should be preserved as empty lists (not nil)
// to prevent perpetual diffs when config has [] but AWS returns []
ExpectedValue: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"arn": tftypes.String,
Expand All @@ -248,7 +250,7 @@ func TestTranslateToTerraform(t *testing.T) {
"identifier": tftypes.NewValue(tftypes.String, nil),
"name": tftypes.NewValue(tftypes.String, "testing"),
"number": tftypes.NewValue(tftypes.Number, 42),
"ports": tftypes.NewValue(tftypes.List{ElementType: tftypes.Number}, nil),
"ports": tftypes.NewValue(tftypes.List{ElementType: tftypes.Number}, []tftypes.Value{}),
}),
},
{
Expand All @@ -260,6 +262,8 @@ func TestTranslateToTerraform(t *testing.T) {
"Name": "testing",
"Number": float64(42),
},
// Missing list fields from AWS should be set to empty list (not null)
// to prevent perpetual diffs when config has [] but AWS omits the field
ExpectedValue: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"arn": tftypes.String,
Expand All @@ -273,7 +277,7 @@ func TestTranslateToTerraform(t *testing.T) {
"identifier": tftypes.NewValue(tftypes.String, nil),
"name": tftypes.NewValue(tftypes.String, "testing"),
"number": tftypes.NewValue(tftypes.Number, 42),
"ports": tftypes.NewValue(tftypes.List{ElementType: tftypes.Number}, nil),
"ports": tftypes.NewValue(tftypes.List{ElementType: tftypes.Number}, []tftypes.Value{}),
}),
},
{
Expand Down