diff --git a/internal/genconfig/generate_config.go b/internal/genconfig/generate_config.go index 5843c88b3e..87507048b9 100644 --- a/internal/genconfig/generate_config.go +++ b/internal/genconfig/generate_config.go @@ -169,8 +169,6 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri // If the value is a string storing a JSON value we want to represent it in a terraform native way // and encapsulate it in `jsonencode` as it is the idiomatic representation if val.IsKnown() && !val.IsNull() && val.Type() == cty.String && json.Valid([]byte(val.AsString())) { - buf.WriteString("jsonencode(") - var ctyValue ctyjson.SimpleJSONValue err := ctyValue.UnmarshalJSON([]byte(val.AsString())) if err != nil { @@ -183,27 +181,25 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri continue } - tok := hclwrite.TokensForValue(ctyValue.Value) - if _, err := tok.WriteTo(buf); err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Skipped part of config generation", - Detail: fmt.Sprintf("Could not create attribute %s in %s when generating import configuration. The plan will likely report the missing attribute as being deleted.", name, addr), - Extra: err, - }) - continue + // Lone deserializable primitive types are valid json, but should be treated as strings + if ctyValue.Type().IsPrimitiveType() { + if d := writeTokens(val, buf); d != nil { + diags = diags.Append(d) + continue + } + } else { + buf.WriteString("jsonencode(") + + if d := writeTokens(ctyValue.Value, buf); d != nil { + diags = diags.Append(d) + continue + } + + buf.WriteString(")") } - - buf.WriteString(")") } else { - tok := hclwrite.TokensForValue(val) - if _, err := tok.WriteTo(buf); err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Skipped part of config generation", - Detail: fmt.Sprintf("Could not create attribute %s in %s when generating import configuration. The plan will likely report the missing attribute as being deleted.", name, addr), - Extra: err, - }) + if d := writeTokens(val, buf); d != nil { + diags = diags.Append(d) continue } } @@ -215,6 +211,20 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri return diags } +func writeTokens(val cty.Value, buf *strings.Builder) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + tok := hclwrite.TokensForValue(val) + if _, err := tok.WriteTo(buf); err != nil { + return diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: "Skipped part of config generation", + Detail: "Could not create attribute in import configuration. The plan will likely report the missing attribute as being deleted.", + Extra: err, + }) + } + return diags +} + func writeConfigBlocks(addr addrs.AbsResourceInstance, buf *strings.Builder, blocks map[string]*configschema.NestedBlock, indent int) tfdiags.Diagnostics { var diags tfdiags.Diagnostics diff --git a/internal/genconfig/generate_config_test.go b/internal/genconfig/generate_config_test.go index a139232677..87d3fb778f 100644 --- a/internal/genconfig/generate_config_test.go +++ b/internal/genconfig/generate_config_test.go @@ -500,6 +500,48 @@ resource "tfcoremock_simple_resource" "empty" { expected: ` resource "tfcoremock_simple_resource" "empty" { value = jsonencode(["Hello", "World"]) +}`, + }, + "simple_resource_with_json_primitive_strings": { + schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "value_string_number": { + Type: cty.String, + Optional: true, + }, + "value_string_bool": { + Type: cty.String, + Optional: true, + }, + }, + }, + addr: addrs.AbsResourceInstance{ + Module: nil, + Resource: addrs.ResourceInstance{ + Resource: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "tfcoremock_simple_resource", + Name: "empty", + }, + Key: nil, + }, + }, + provider: addrs.LocalProviderConfig{ + LocalName: "tfcoremock", + }, + value: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("D2320658"), + "value_string_number": cty.StringVal("42"), + "value_string_bool": cty.StringVal("true"), + }), + expected: ` +resource "tfcoremock_simple_resource" "empty" { + value_string_bool = "true" + value_string_number = "42" }`, }, "simple_resource_with_malformed_json": {