skip to Main Content

I have the below body used in a resource in terraform to deploy logic app:

body = jsonencode({
    properties = {
      definition = jsondecode(templatefile(templ.json,
        {
          schedHour     = tostring(var.schedule_hour) #is a string type;
          functUri      = "https://abc"
          reqBody       = jsonencode(var.func_parameters)   #is an object type;
      }))  
      state = "Enabled"
    }
  })

The problem is when I want to deploy that JSON template where I reference the reqBody value as below:

"inputs": {
        "subscribe": {
          "body": "${reqBody}",
          "method": "POST",
          "uri": "${functUri}"
        },
        "unsubscribe": {}
      }

It results the error when I try terraform apply:

Error: Error in function call
    
  on ../modules/logic-app/main.tf line 21, in resource "azapi_resource" "logic_app_workflow":
  21:       definition = jsondecode(templatefile(templ.json,
  22:         {
  23:           scheduleHour     = tostring(var.schedule_hour)
  24:           functUri   = "https://abc"
  27:           reqBody      = jsonencode(var.func_parameters)
  28:       }))
    ├────────────────
    │ while calling jsondecode(str)
    │ var.func_parameters is object with 1 attribute "pipeline"
    │ var.schedule_hour is 5

Call to function "jsondecode" failed: invalid character 'p' after object key:value pair.

It seems the problem is on that object variable reqBody which is as below defined:

variable "func_parameters" {
  type = object({
    pipeline = object({
      calc_date = string
      ....
    })
  })
}

One possible FIX but wished/right one is to use in json template the reference as:
"body" : ${reqBody} #without "" but it makes to be an invalid json template but it works to be deployed;

I tried move the reqBody param in a separately block called parameters but not working. it should be in definition, as azapi_resource require.

For the "uri" : "${fctUri}" works, it’s fine referenced and shows what is needed (it is a string), but the problem remains on that object var;
ref: https://github.com/michaelstephensonuk/CodeSnippets/blob/main/LogicApp-Consumption-WithTerraform/Res.LogicApp.Demo-LogicApp-2.tf

I tried with jsondecode for that object var and the error is saying it should be a string;
somehow it requires a string as for the rest variables: I tried with decode for that object

on ../modules/logic-app/main.tf line 27, in resource "azapi_resource" "logic_app_workflow":
  27:           requestBody      = jsondecode(var.func_parameters)
    ├────────────────
    │ while calling jsondecode(str)
    │ var.func_parameters is a object
 
Invalid value for "str" parameter: string required.

the rest are strings and they works fine;

2

Answers


  1. You have to break your code and add outputs to see what is going on…
    all those nested functions are just obscuring the issue, use multiple local variables, like:

    variable "func_parameters" {
      type = object({
        pipeline = object({
          calc_date = string
        })
      })
      default = {
        pipeline = {
          calc_date = "test"
        }
      }
    }
    variable "schedule_hour" {
      default = "12"
    }
    
    locals {
      data = templatefile("templ.json",
        {
          schedHour = tostring(var.schedule_hour)
          functUri  = "https://abc"
          reqBody   = jsonencode(var.func_parameters)
        }
      )
      body = jsonencode({
        properties = {
          definition = jsondecode(local.data)
          state      = "Enabled"
        }
      })
    }
    
    output "test" {
      value = local.data
    }
    

    the json I’m using:

    {
        "inputs": {
            "subscribe": {
                "body": "${reqBody}",
                "method": "POST",
                "uri": "${functUri}"
            },
            "unsubscribe": {}
        }
    }
    

    yes that will error out but we also get some output:

    Changes to Outputs:
      + test = <<-EOT
            {
                "inputs": {
                    "subscribe": {
                        "body": "{"pipeline":{"calc_date":"test"}}",
                        "method": "POST",
                        "uri": "https://abc"
                    },
                    "unsubscribe": {}
                }
            }
        EOT
    

    We can see that is not valid json there are unescaped quotes there


    To get around the unescaped quotes you need to do:
    replace(jsonencode(var.func_parameters), """, "\"")

    full code:

    variable "func_parameters" {
      type = object({
        pipeline = object({
          calc_date = string
        })
      })
      default = {
        pipeline = {
          calc_date = "test"
        }
      }
    }
    variable "schedule_hour" {
      default = "12"
    }
    
    locals {
      data = templatefile("templ.json",
        {
          schedHour = tostring(var.schedule_hour)
          functUri  = "https://abc"
          reqBody   = replace(jsonencode(var.func_parameters), """, "\"")
        }
      )
      body = jsonencode({
        properties = {
          definition = jsondecode(local.data)
          state      = "Enabled"
        }
      })
    }
    
    output "test" {
      value = local.body
    }
    
    Login or Signup to reply.
  2. Generating valid JSON by concatenating strings together is complicated to perform correctly, because JSON expects values to be escaped in a specific way, etc.

    If you instead use jsonencode to produce the JSON from your template, you can be sure that the result will be valid JSON because Terraform itself will create the JSON string:

    ${jsonencode({
     inputs = {
       subscribe = {
         body   = reqBody
         method = "POST"
         uri    = functUri
       }
       unsubscribe = {}
     }
    })}
    

    For this template, the entire template is a single interpolation sequence ${ ... } which contains just a call to jsonencode. The result of jsonencode is a JSON representation of its argument, and so the template result will be a JSON representation of the result of the Terraform expression passed to that function.

    Because jsonencode always produces valid JSON, jsondecode of that result should therefore always succeed.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search