Terraformのjsonencode関数が特殊文字をエスケーする問題と回避策

Terraformのjsonencode関数でjson文字列を生成するとき、 < > & U+2028 U+2029エスケープされる。

developer.hashicorp.com

このため、例えば aws_cloudwatch_event_target の input_transformerを用いて送信メッセージをカスタマイズしたい場合に < > を利用すると上手く動作しなくなる。

resource "aws_cloudwatch_event_target" "http" {
  event_bus_name = "default"
  arn            = aws_cloudwatch_event_api_destination.this.arn
  rule           = aws_cloudwatch_event_rule.this.name
  role_arn       = aws_iam_role.this.arn

  input_transformer {
    input_paths = {sender_ip = "$.detail.senderIP"}
    input_template = jsonencode(  # <------------ This is where the problem occurs
      {message = "Connection from <sender_ip>"}
    )
  }
}

github.com

回避策としては、ヒアドキュメントを利用するかreplace関数を利用するか。

# ヒアドキュメントを利用
resource "aws_cloudwatch_event_target" "http" {
  event_bus_name = "default"
  arn            = aws_cloudwatch_event_api_destination.this.arn
  rule           = aws_cloudwatch_event_rule.this.name
  role_arn       = aws_iam_role.this.arn

  input_transformer {
    input_paths = {sender_ip = "$.detail.senderIP"}
    input_template = <<<EOT
{
  "message": "Connection from <sender_ip>"
}
EOT
  }
}
# replace関数を利用
resource "aws_cloudwatch_event_target" "http" {
  event_bus_name = "default"
  arn            = aws_cloudwatch_event_api_destination.this.arn
  rule           = aws_cloudwatch_event_rule.this.name
  role_arn       = aws_iam_role.this.arn

  input_transformer {
    input_paths = {sender_ip = "$.detail.senderIP"}
    input_template = replace(replace(jsonencode({message = "Connection from <sender_ip>"}), "\\u003c", "<"), "\\u00e3", ">")
  }
}

また、こちらのissueにコメントがある通り、Terraform 1.8からは Provider-defined functionsもサポートされたので、terraform-provider-aws 側にてカスタム関数を構築して実装を容易にするアプローチも考えられるらしい。 terraform-provider-aws リポジトリでは新しくnew-functionラベルが生まれたが、このアプローチはIssue化まではされていない。