Skip to content

terraform plan -generate-config-out fails for map nested objects with keys with spaces #35752

@henryrecker-pingidentity

Description

Terraform Version

Terraform v1.9.5
on darwin_amd64

Terraform Configuration Files

provider "example" {
}

import {
  to = example_example.myExample
  id = "str"
}

Debug Output

https://gist.github.com/henryrecker-pingidentity/ae09eb59b71a2d0c02526018760dd254

Expected Behavior

HCL should be generated with the expected values

Actual Behavior

Error, no HCL is generated:

Planning failed. Terraform encountered an error while generating this plan.

╷
│ Error: Missing key/value separator
│
│   on generated_resources.tf line 2:
│   (source code not available)
│
│ Expected an equals sign ("=") to mark the beginning of the attribute value. If you intended to given an
│ attribute name containing periods or spaces, write the name in quotes to create a string literal.
╵

Steps to Reproduce

terraform plan -generate-config-out=generated_resources.tf with a provider schema that has a MapNestedAttribute with a key that includes spaces.

A simple provider that reproduces this issue can be found here: https://github.com/henryrecker-pingidentity/terraform-provider-example/tree/GenerateConfigBug

Relevant provider code, note the "Use Case" name with the space.

func (r *exampleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Description: "Example resource.",
		Attributes: map[string]schema.Attribute{
			"string_val": schema.StringAttribute{
				Description: "Optional string attribute",
				Optional:    true,
			},
			"map_val": schema.MapNestedAttribute{
				NestedObject: schema.NestedAttributeObject{
					Attributes: map[string]schema.Attribute{
						"values": schema.SetAttribute{
							ElementType: types.StringType,
							Optional:    true,
							Description: "A Set of values",
						},
					},
				},
				Optional:    true,
				Description: "Extended Properties allows to store additional information for IdP/SP Connections. The names of these extended properties should be defined in /extendedProperties.",
			},
		},
	}
}

// Metadata returns the resource type name.
func (r *exampleResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
	resp.TypeName = req.ProviderTypeName + "_example"
}

func (r *exampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
	plan := exampleResourceModel{
		StringVal: types.StringValue("str"),
		MapVal: types.MapValueMust(types.ObjectType{
			AttrTypes: map[string]attr.Type{
				"values": types.SetType{
					ElemType: types.StringType,
				},
			}}, map[string]attr.Value{
			"Use Case": types.ObjectValueMust(map[string]attr.Type{
				"values": types.SetType{
					ElemType: types.StringType,
				},
			}, map[string]attr.Value{
				"values": types.SetValueMust(types.StringType, []attr.Value{
					types.StringValue("CIAM"),
				}),
			}),
		}),
	}
	diags := resp.State.Set(ctx, plan)
	resp.Diagnostics.Append(diags...)
}

func (r *exampleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
	plan := exampleResourceModel{
		StringVal: types.StringValue("str"),
		MapVal: types.MapValueMust(types.ObjectType{
			AttrTypes: map[string]attr.Type{
				"values": types.SetType{
					ElemType: types.StringType,
				},
			}}, map[string]attr.Value{
			"Use Case": types.ObjectValueMust(map[string]attr.Type{
				"values": types.SetType{
					ElemType: types.StringType,
				},
			}, map[string]attr.Value{
				"values": types.SetValueMust(types.StringType, []attr.Value{
					types.StringValue("CIAM"),
				}),
			}),
		}),
	}
	diags := resp.State.Set(ctx, plan)
	resp.Diagnostics.Append(diags...)
}

// Update updates the resource and sets the updated Terraform state on success.
func (r *exampleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
	plan := exampleResourceModel{
		StringVal: types.StringValue("str"),
		MapVal: types.MapValueMust(types.ObjectType{
			AttrTypes: map[string]attr.Type{
				"values": types.SetType{
					ElemType: types.StringType,
				},
			}}, map[string]attr.Value{
			"Use Case": types.ObjectValueMust(map[string]attr.Type{
				"values": types.SetType{
					ElemType: types.StringType,
				},
			}, map[string]attr.Value{
				"values": types.SetValueMust(types.StringType, []attr.Value{
					types.StringValue("CIAM"),
				}),
			}),
		}),
	}
	diags := resp.State.Set(ctx, plan)
	resp.Diagnostics.Append(diags...)
}

// No backend so no logic needed
func (r *exampleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
}

func (r *exampleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
	resource.ImportStatePassthroughID(ctx, path.Root("string_val"), req, resp)
}

Additional Context

No response

References

No response

Metadata

Metadata

Assignees

Labels

bugconfirmeda Terraform Core team member has reproduced this issuenewnew issue not yet triaged

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions