diff --git a/internal/genconfig/generate_config.go b/internal/genconfig/generate_config.go index b0be1d0218..7aebfba0c4 100644 --- a/internal/genconfig/generate_config.go +++ b/internal/genconfig/generate_config.go @@ -100,7 +100,7 @@ func GenerateResourceContents(addr addrs.AbsResourceInstance, diags = diags.Append(writeConfigAttributes(addr, &buf, schema.Attributes, 2)) diags = diags.Append(writeConfigBlocks(addr, &buf, schema.BlockTypes, 2)) } else { - diags = diags.Append(writeConfigAttributesFromExisting(addr, &buf, stateVal, schema.Attributes, 2, optionalOrRequiredProcessor)) + diags = diags.Append(writeConfigAttributesFromExisting(addr, &buf, stateVal, schema.Attributes, 2)) diags = diags.Append(writeConfigBlocksFromExisting(addr, &buf, stateVal, schema.BlockTypes, 2)) } @@ -185,7 +185,7 @@ func generateImportBlock(addr addrs.AbsResourceInstance, idSchema *configschema. buf.WriteString(fmt.Sprintf(" to = %s\n", addr.String())) buf.WriteString(fmt.Sprintf(" provider = %s\n", pc.StringCompact())) buf.WriteString(" identity = {\n") - diags = diags.Append(writeConfigAttributesFromExisting(addr, &buf, identity, idSchema.Attributes, 2, allowAllAttributesProcessor)) + diags = diags.Append(writeConfigAttributesFromExisting(addr, &buf, identity, idSchema.Attributes, 2)) buf.WriteString(strings.Repeat(" ", 2)) buf.WriteString("}\n}\n") @@ -240,16 +240,7 @@ func writeConfigAttributes(addr addrs.AbsResourceInstance, buf *strings.Builder, return diags } -func optionalOrRequiredProcessor(attr *configschema.Attribute) bool { - // Exclude computed-only attributes - return attr.Optional || attr.Required -} - -func allowAllAttributesProcessor(attr *configschema.Attribute) bool { - return true -} - -func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, stateVal cty.Value, attrs map[string]*configschema.Attribute, indent int, processAttr func(*configschema.Attribute) bool) tfdiags.Diagnostics { +func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, stateVal cty.Value, attrs map[string]*configschema.Attribute, indent int) tfdiags.Diagnostics { var diags tfdiags.Diagnostics if len(attrs) == 0 { return diags @@ -258,66 +249,72 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri // Sort attribute names so the output will be consistent between runs. for _, name := range slices.Sorted(maps.Keys(attrs)) { attrS := attrs[name] + + var val cty.Value + if !stateVal.IsNull() && stateVal.Type().HasAttribute(name) { + val = stateVal.GetAttr(name) + } else { + val = attrS.EmptyValue() + } + + if attrS.Computed && val.IsNull() { + // Computed attributes should never be written in the config. These + // will be filtered out of the given cty value here, but we should + // also skip writing `null` in the config. + continue + } + if attrS.NestedType != nil { writeConfigNestedTypeAttributeFromExisting(addr, buf, name, attrS, stateVal, indent) continue } - if processAttr != nil && processAttr(attrS) { - buf.WriteString(strings.Repeat(" ", indent)) - buf.WriteString(fmt.Sprintf("%s = ", name)) - - var val cty.Value - if !stateVal.IsNull() && stateVal.Type().HasAttribute(name) { - val = stateVal.GetAttr(name) - } else { - val = attrS.EmptyValue() - } + buf.WriteString(strings.Repeat(" ", indent)) + buf.WriteString(fmt.Sprintf("%s = ", name)) + + if attrS.Sensitive { + buf.WriteString("null # sensitive") + } else { + // 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())) { + var ctyValue ctyjson.SimpleJSONValue + err := ctyValue.UnmarshalJSON([]byte(val.AsString())) + if err != nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: "Failed to parse JSON", + Detail: fmt.Sprintf("Could not parse JSON value of attribute %s in %s when generating import configuration. The plan will likely report the missing attribute as being deleted. This is most likely a bug in Terraform, please report it.", name, addr), + Extra: err, + }) + continue + } - if attrS.Sensitive { - buf.WriteString("null # sensitive") - } else { - // 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())) { - var ctyValue ctyjson.SimpleJSONValue - err := ctyValue.UnmarshalJSON([]byte(val.AsString())) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Failed to parse JSON", - Detail: fmt.Sprintf("Could not parse JSON value of attribute %s in %s when generating import configuration. The plan will likely report the missing attribute as being deleted. This is most likely a bug in Terraform, please report it.", name, addr), - Extra: err, - }) + // 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 } - - // 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(")") - } } else { - if d := writeTokens(val, buf); d != nil { + buf.WriteString("jsonencode(") + + if d := writeTokens(ctyValue.Value, buf); d != nil { diags = diags.Append(d) continue } + + buf.WriteString(")") + } + } else { + if d := writeTokens(val, buf); d != nil { + diags = diags.Append(d) + continue } } - - buf.WriteString("\n") } + + buf.WriteString("\n") } return diags } @@ -451,7 +448,6 @@ func writeConfigBlocksFromExisting(addr addrs.AbsResourceInstance, buf *strings. func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, name string, schema *configschema.Attribute, stateVal cty.Value, indent int) tfdiags.Diagnostics { var diags tfdiags.Diagnostics - processor := optionalOrRequiredProcessor switch schema.NestedType.Nesting { case configschema.NestingSingle: @@ -479,7 +475,7 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance, buf.WriteString(strings.Repeat(" ", indent)) buf.WriteString(fmt.Sprintf("%s = {\n", name)) - diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, nestedVal, schema.NestedType.Attributes, indent+2, processor)) + diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, nestedVal, schema.NestedType.Attributes, indent+2)) buf.WriteString("}\n") return diags @@ -505,7 +501,7 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance, buf.WriteString(strings.Repeat(" ", indent+2)) buf.WriteString("{\n") - diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, listVals[i], schema.NestedType.Attributes, indent+4, processor)) + diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, listVals[i], schema.NestedType.Attributes, indent+4)) buf.WriteString(strings.Repeat(" ", indent+2)) buf.WriteString("},\n") } @@ -537,7 +533,7 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance, buf.WriteString(fmt.Sprintf("%s = {", hclEscapeString(key))) buf.WriteString("\n") - diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, vals[key], schema.NestedType.Attributes, indent+4, processor)) + diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, vals[key], schema.NestedType.Attributes, indent+4)) buf.WriteString(strings.Repeat(" ", indent+2)) buf.WriteString("}\n") } @@ -553,7 +549,6 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance, func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, name string, schema *configschema.NestedBlock, stateVal cty.Value, indent int) tfdiags.Diagnostics { var diags tfdiags.Diagnostics - processAttr := optionalOrRequiredProcessor switch schema.Nesting { case configschema.NestingSingle, configschema.NestingGroup: @@ -564,7 +559,7 @@ func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *str buf.WriteString(fmt.Sprintf("%s {", name)) buf.WriteString("\n") - diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, stateVal, schema.Attributes, indent+2, processAttr)) + diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, stateVal, schema.Attributes, indent+2)) diags = diags.Append(writeConfigBlocksFromExisting(addr, buf, stateVal, schema.BlockTypes, indent+2)) buf.WriteString("}\n") return diags @@ -573,7 +568,7 @@ func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *str for i := range listVals { buf.WriteString(strings.Repeat(" ", indent)) buf.WriteString(fmt.Sprintf("%s {\n", name)) - diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, listVals[i], schema.Attributes, indent+2, processAttr)) + diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, listVals[i], schema.Attributes, indent+2)) diags = diags.Append(writeConfigBlocksFromExisting(addr, buf, listVals[i], schema.BlockTypes, indent+2)) buf.WriteString("}\n") } @@ -584,7 +579,7 @@ func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *str buf.WriteString(strings.Repeat(" ", indent)) buf.WriteString(fmt.Sprintf("%s %q {", name, key)) buf.WriteString("\n") - diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, vals[key], schema.Attributes, indent+2, processAttr)) + diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, vals[key], schema.Attributes, indent+2)) diags = diags.Append(writeConfigBlocksFromExisting(addr, buf, vals[key], schema.BlockTypes, indent+2)) buf.WriteString(strings.Repeat(" ", indent)) buf.WriteString("}\n")