How to jsonencode the return value of templatefile

Context

I am creating aws_dms_replication_tasks with a for_each, trying to build the required table_mappings from a template file. The mappings must be JSON so I did

table_mappings = jsonencode(templatefile("./tableMappings.tftpl", each.value.table_mappings))

Problem

Doing the above results in the following error: InvalidParameterValueException: Invalid Table Mappings document. Invalid json Invalid json object and only references the resource I am trying to create, no indicator of where the problem is in the JSON.

To ensure that there is not a problem with my template I reduced it to a very simple case and ran it through a JSON validator. I still get the error, so I know the problem is with the way I attempted to convert the template result. Simplified tableMappings.tftpl for reference:

{
    "rules": [
        {
            "rule-type": "transformation",
            "rule-id": "1",
            "rule-name": "1",
            "rule-action": "add-column",
            "rule-target": "column",
            "object-locator": {
                "schema-name": "example",
                "table-name": "%"
            },
            "value": "orgId",
            "expression": "'orgId-001'",
            "data-type": {
                "type": "string",
                "length": 36
            }
        }
    ]
}

Question

How can I correctly JSON encode the result of templatefile()?

The underlying issue

I am aware that the normal practice is to wrap the template with ${jsonencode(...)}, which does prevent the error. However, I need to create a list of rules in the template where two of them are static and the rest are formed by a loop. I can do this with template syntax, but wrapping with jsonencode puts me in expression syntax, and in expression syntax results of loops must come back enclosed in a list or object so I would end up with the incorrect result:

"rules": [
    { ...rule 1},
    { ...rule 2},
    [ ...rules from loop stuck in here ]
]

I have a couple ideas for workarounds, but I would appreciate a way to do this without cluttering up my template. Thanks!

Hi @johntreez,

It seems like your template is already trying to produce a JSON string, so I don’t think you should need to do any further JSON encoding to it as long as your template handles the JSON syntax generation correctly: you should be able to just pass the templatefile result directly to any argument that expects a JSON string.

If you’re still interested in trying to solve this with a jsonencode inside your template though, one thing you could try is using the flatten function in your rules expression to flatten the list generated by your for expression into the top-level list:

${jsonencode({
  rules = flatten([
    { /* ... */ },
    { /* ... */ },
    [ for foo in bar : { /* ... */ } ],
  ])
})}
1 Like

Hi @apparentlymart,

I did try passing the result directly before and got a contains an invalid JSON: invalid character error which I assumed (incorrectly) was an encoding error. Due to your suggestion though, I went back and tried a simplified case and it worked, which led to me finding a syntax error in the commit where I tried it the first time. I fixed the error and now it is working correctly. Thanks!

I had to re-order (loop first) to avoid trailing commas, but that’s less grating than having to stick a workaround in there (I was using concat not flatten but those are both fairly clean as far as workarounds go).

tl;dr Suggestion works, I just had a syntax error when I originally tried it. Workarounds with flatten or concat would do the trick as well.