diff --git a/plans/objchange/normalize_obj.go b/plans/objchange/normalize_obj.go index 3e1eee815..baf799fec 100644 --- a/plans/objchange/normalize_obj.go +++ b/plans/objchange/normalize_obj.go @@ -34,6 +34,16 @@ func NormalizeObjectFromLegacySDK(val cty.Value, schema *configschema.Block) cty } for name, blockS := range schema.BlockTypes { lv := val.GetAttr(name) + + // Legacy SDK never generates dynamically-typed attributes and so our + // normalization code doesn't deal with them, but we need to make sure + // we still pass them through properly so that we don't interfere with + // objects generated by other SDKs. + if ty := blockS.Block.ImpliedType(); ty.HasDynamicTypes() { + vals[name] = lv + continue + } + switch blockS.Nesting { case configschema.NestingSingle: if lv.IsKnown() { diff --git a/plans/objchange/normalize_obj_test.go b/plans/objchange/normalize_obj_test.go index f95a0b460..abc9c3770 100644 --- a/plans/objchange/normalize_obj_test.go +++ b/plans/objchange/normalize_obj_test.go @@ -224,6 +224,74 @@ func TestNormalizeObjectFromLegacySDK(t *testing.T) { }), }), }, + "block list with dynamic type": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.DynamicPseudoType, Optional: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.True, + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.True, + }), + }), + }), + }, + "block map with dynamic type": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingMap, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.DynamicPseudoType, Optional: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "one": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + "another": cty.ObjectVal(map[string]cty.Value{ + "b": cty.True, + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "one": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + "another": cty.ObjectVal(map[string]cty.Value{ + "b": cty.True, + }), + }), + }), + }, } for name, test := range tests {