CR/LF vs LF handling with resource string attributes

Problem:

in my provider, a resource has a config attribute, which can take an arbitrary text (think “multi-line network device configuration”). This is passed to the back end and stored there. When reading the resource, the config has always Unix line feeds. This is fine as long as the original configuration also has Unix line feeds. However, a Windows user has raised an issue where their configuration files (read via file(filename) has Windows/DOS line endings and of course the string comparison fails in this case. Which is flagged as a bug in the provider… well.

Workaround:

config = replace(file(var.filename), "\r\n", "\n") but this seems inconvenient and is likely not what the user expects.

Ideally, there would be some file_text(var.filename) variant which converts CR/LF to LF to make the text files consistent (which would make this an HCL question and that solution is going to be rather unlikely to happen).

Question:

Is there anything else I could do within the provider to address this? Mangling the returned data from the API to insert carriage returns seems to be odd and it would also require some additional flag / attribute to indicate the requirement for such a modification.

Hi @rschmied :wave:

Ideally, there would be some file_text(var.filename) variant which converts CR/LF to LF to make the text files consistent (which would make this an HCL question and that solution is going to be rather unlikely to happen).

One option with respect to using a function to replace CR/LF with LF would be to implement a provider-defined function on the provider itself. This would allow for syntax such as the following to be used:

resource "example_resource" "thing" {
  config = provider::example::file_text(var.filename)
}

Another option might be to set-up the schema for the resource so that you have something like the following:

func (e *exampleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			"config_raw": schema.StringAttribute{
				Required: true,
			},
			"config_cr": schema.StringAttribute{
				Computed: true,
			},
			"config_cr_lf": schema.StringAttribute{
				Computed: true,
			},

This would store the “raw” value of the config, along with the computed values containing either CR and LF, or just LF.

If the remote API considers \n and \r\n to be equivalent – that is, two different ways of expressing the same meaning rather than two different meanings – then this seems like a normalization-shaped problem and so something that could be handled using a semantic equality rule.

With such a rule in place, Terraform would treat the value as unchanged as long as it differs only in the choice of line endings, without the author needing to write anything special to force that normalization.

1 Like

thanks for this @bendbennett – I didn’t know about the provider defined functions! However, I think that @apparentlymart 's proposal is the way to go (at least for my specific use case). Thanks again and have a great day!

thanks – this looks promising and is actually the information I was hoping for. I’ll try to implement this!